Documente Academic
Documente Profesional
Documente Cultură
Programare C PDF
Programare C PDF
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);
__________________________________________________________________________
__________________________________________________________________________
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 flotante
Constante ntregi
O constant ntreag const dintr-o succesiune de cifre.
O constant octal este o constant ntreag care ncepe cu 0
(cifra zero), i este format cu cifre de la 0 la 7.
O constant hexazecimal este o constant ntreag precedat de
0x sau 0X (cifra 0 i litera x). Cifrele hexazecimale includ literele de
la A la F i de la a la f cu valori de la 10 la 15.
n orice alt caz, constanta ntreag este o constant zecimal.
Exemplu: constanta zecimal 31 poate fi scris ca 037 n octal i
0x1f sau 0X1F n hexazecimal.
O constant ntreag este generat pe un cuvnt (doi sau patru
octei, dac sistemul de calcul este pe 16 sau 32 de bii).
O constant zecimal a crei valoare depete pe cel mai mare
ntreg cu semn reprezentabil pe un cuvnt scurt (16 bii) se consider
de tip long i este generat pe 4 octei.
O constant octal sau hexazecimal care depete pe cel mai
mare ntreg fr semn reprezentabil pe un cuvnt scurt se consider de
asemenea de tip long.
O constant ntreag devine negativ dac i se aplic operatorul
unar de negativare -.
Constante 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:
__________________________________________________________________________
__________________________________________________________________________
11
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).
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;
__________________________________________________________________________
__________________________________________________________________________
13
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
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.
__________________________________________________________________________
__________________________________________________________________________
17
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.
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.
__________________________________________________________________________
__________________________________________________________________________
19
20
Tipul ntreg
__________________________________________________________________________
__________________________________________________________________________
21
22
/* p indic pe x */
/* p indic pe r */
Tipuri derivate
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
__________________________________________________________________________
__________________________________________________________________________
23
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 216 sau 232).
ntr-o reprezentare la complementul fa de 2 (deci pentru numere
negative), conversia este conceptual, nu exist nici o schimbare
real a 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.
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];
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
26
__________________________________________________________________________
__________________________________________________________________________
27
28
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.
__________________________________________________________________________
__________________________________________________________________________
29
30
__________________________________________________________________________
__________________________________________________________________________
31
32
__________________________________________________________________________
__________________________________________________________________________
33
34
&
0
1
0
0
0
1
0
1
Expresie-SAU-inclusiv:
expresie | expresie
Operatorul | este operatorul SAU-inclusiv logic pe bii. El este
asociativ i expresiile care-l conin pot fi rearanjate. Rezultatul este
funcia logic SAU-inclusiv pe bii aplicat operanzilor si.
Operatorul se aplic numai la operanzi de tipuri ntregi. Legea dup
care funcioneaz este:
|
0
1
1
1
0
Expresie-I:
expresie & expresie
Operatorul & este operatorul I logic pe bii. El este asociativ i
expresiile care conin operatorul & pot fi rearanjate. Rezultatul este
funcia logic I pe bii aplicat operanzilor si. Operatorul se
aplic numai la operanzi de tipuri ntregi. Legea dup care
funcioneaz este:
0
0
1
0
0
1
1
1
1
Expresie-SAU-exclusiv:
expresie ^ expresie
Expresie-I-logic:
expresie && expresie
Operatorul && este operatorul I-logic i el se grupeaz de la
stnga la dreapta. Rezultatul este 1 dac ambii operanzi snt diferii de
__________________________________________________________________________
__________________________________________________________________________
35
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
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:
__________________________________________________________________________
__________________________________________________________________________
41
42
identificator
(declarator)
* declarator
declarator ()
declarator [expresie-constantopt]
register
int sp;
double val[MAXVAL];
unsigned
__________________________________________________________________________
__________________________________________________________________________
43
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
*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
__________________________________________________________________________
__________________________________________________________________________
47
48
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.
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
50
List-instruciuni:
instruciune
instruciune list-instruciuni
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
52
Format:
while (expresie)
instruciune
Instruciunea se execut repetat atta timp ct valoarea expresiei
este diferit de zero. Testul are loc naintea fiecrei execuii a
instruciunii. Prin urmare ciclul este urmtorul: se testeaz condiia
din paranteze dac ea este adevrat, deci expresia din paranteze are
o valoare diferit de zero, se execut corpul instruciunii while, se
verific din nou condiia, dac ea este adevrat se execut din nou
corpul instruciunii. Cnd condiia devine fals, adic valoarea
expresiei din paranteze este zero, se face un salt la instruciunea de
dup corpul instruciunii while, deci instruciunea while se
termin.
6.5. Instruciunea do
Format:
do instruciune while
(expresie);
__________________________________________________________________________
__________________________________________________________________________
53
54
__________________________________________________________________________
__________________________________________________________________________
55
56
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
for (...) {
...
contin:;
}
do {
...
contin:;
} while (...);
__________________________________________________________________________
__________________________________________________________________________
57
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.
s[i]=='\t'; i++) ;
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';
/* punct zecimal */
if (s[i]== '.')
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;
}
__________________________________________________________________________
__________________________________________________________________________
67
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
#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;
__________________________________________________________________________
__________________________________________________________________________
73
74
if (p==0) return q;
else return p;
}
9. Pointeri i masive
__________________________________________________________________________
__________________________________________________________________________
75
76
y = *px + 1;
unde variabilei y i se atribuie coninutul variabilei x plus 1.
Instruciunea:
d = sqrt((double)*px);
are ca efect convertirea coninutului variabilei x pe care o indic px
n tip double i apoi depunerea rdcinii ptrate a valorii astfel
convertite n variabila d.
Referiri la pointeri pot aprea de asemenea i n partea stng a
atribuirilor. Dac, de exemplu, px indic spre x, atunci:
*px = 0;
atribuie variabilei x valoarea zero, iar:
*px += 1;
incrementeaz coninutul variabilei x cu 1, ca i n expresia:
(*px)++;
n acest ultim exemplu parantezele snt obligatorii deoarece, n
lipsa lor, expresia ar incrementa pe px n loc de coninutul variabilei
pe care o indic (operatorii unari *, ++ au aceeai preceden i snt
evaluai de la dreapta spre stnga).
/* greit */
__________________________________________________________________________
__________________________________________________________________________
77
78
__________________________________________________________________________
__________________________________________________________________________
79
80
}
free(char *p) { /* elibereaz memoria indicat de p */
if (p>=allocbuf && p<allocbuf+ALLOCSIZE)
allocp = p;
}
Testul if (allocp+n<=allocbuf+ALLOCSIZE)
verific dac exist spaiu suficient pentru satisfacerea cererii de
alocare a n caractere. Dac cererea poate fi satisfcut, alloc revine
cu un pointer la zona de n caractere consecutive. Dac nu, alloc
trebuie s semnaleze lipsa de spaiu pe care o face returnnd valoarea
constantei simbolice NULL. Limbajul C garanteaz c nici un pointer
care indic corect o dat nu va conine zero, prin urmare o revenire
cu valoarea zero poate fi folosit pentru semnalarea unui eveniment
anormal (n cazul nostru, lipsa de spaiu). Atribuirea valorii zero unui
pointer este deci un caz special.
Observm de asemenea c variabilele allocbuf i allocp
snt declarate static cu scopul ca ele s fie locale numai fiierului
surs care conine funciile alloc i free.
Exemplul de mai sus demonstreaz cteva din facilitile
aritmeticii de adrese (pointeri). n primul rnd, pointerii pot fi
comparai n anumite situaii. Dac p i q snt pointeri la membri
unui acelai masiv, atunci relaiile <, <=, >, >=, ==, != snt
valide. Relaia p<q, de exemplu, este adevrat dac p indic un
element mai apropiat de nceputul masivului dect elementul indicat
de pointerul q. Comparrile ntre pointeri pot duce ns la rezultate
imprevizibile, dac ei se refer la elemente aparinnd la masive
diferite.
Se observ c pointerii i ntregii pot fi adunai sau sczui.
Construcia de forma:
p+n
nseamn adresa celui de-al n-lea element dup cel indicat de p,
indiferent de tipul elementului pe care l indic p. Compilatorul C
aliniaz valoarea lui n conform dimensiunii elementelor pe care le
__________________________________________________________________________
__________________________________________________________________________
81
82
__________________________________________________________________________
__________________________________________________________________________
83
84
__________________________________________________________________________
__________________________________________________________________________
85
86
(year%400==0);
for (i=1; yearday>day_tab[leap][i]; i++)
yearday -= day_tab[leap][i];
*pmonth = i;
*pday = yearday;
}
Deoarece aceast ultim funcie returneaz dou valori,
argumentele lun i zi vor 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.
__________________________________________________________________________
__________________________________________________________________________
87
88
__________________________________________________________________________
__________________________________________________________________________
89
90
char *temp;
for (gap=n/2; gap>0; gap/=2)
for (i=gap; i<n; i++)
for (j=i-gap; j>=0; j-=gap) {
if (strcmp(v[j],v[j+gap])<=0)
break;
temp = v[j];
v[j] = v[j+gap];
v[j+gap] = temp;
}
}
Deoarece 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:
__________________________________________________________________________
__________________________________________________________________________
91
92
int x[3][5];
x este un masiv de ntregi, de rangul 3*5. Cnd x apare ntr-o
expresie, el este convertit ntr-un pointer la (primul din cele trei)
masive de 5 ntregi.
__________________________________________________________________________
__________________________________________________________________________
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);
}
}
}
__________________________________________________________________________
__________________________________________________________________________
101
102
funcpt();
Ca un exemplu, s considerm procedura de sortare a liniilor de
la intrare, descris n seciunea 9.7, dar modificat n sensul ca dac
argumentul opional -n apare n linia de comand, atunci liniile se
vor sorta nu lexicografic ci numeric, liniile coninnd grupe de
numere.
O sortare const adesea din trei pri: o comparare care determin
ordinea oricrei perechi de elemente, un schimb care inverseaz
ordinea elementelor implicate i un algoritm de sortare care face
comparrile i inversrile pn cnd elementele snt aduse n ordinea
cerut. Algoritmul de sortare este independent de operaiile de
comparare i inversare, astfel nct transmind diferite funcii de
comparare i inversare funciei de sortare, elementele de intrare se
pot aranja dup diferite criterii.
Compararea lexicografic a dou linii se realizeaz prin funciile
strcmp i swap. Mai avem nevoie de o rutin numcmp care s
compare dou linii pe baza valorilor numerice i care s returneze
aceiai indicatori ca i rutina strcmp.
Declarm aceste trei funcii n funcia principal main, iar
pointerii la aceste funcii i transmitem ca argumente funciei sort,
care la rndul ei va apela aceste funcii prin intermediul pointerilor
respectivi.
Funcia principal main va avea atunci urmtorul cod:
#define LINES 100
if ((nlines=readlines(lineptr,LINES))>=0)
{
if (numeric)
sort(lineptr,nlines,numcmp,swap);
else
sort(lineptr,nlines,strcmp,swap);
writelines (lineptr,nlines);
}
else
printf
("Nr de linii de intrare prea mare\n");
}
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);
}
}
__________________________________________________________________________
__________________________________________________________________________
103
104
if (comp(v[j],v[j+gap])<=0)
nseamn apelul funciei comp (adic strcmp sau numcmp),
deoarece comp este un pointer la funcie, *comp este funcia, iar
comp(v[j],v[j+gap])
este apelul funciei.
exch(v+j,v+j+gap)
este apelul funciei swap, de inversare a dou linii, inversare care
realizeaz interschimbarea adreselor liniilor implicate (vezi seciunea
9.2). Funcia numcmp este urmtoarea:
numcmp(char *s1, char *s2) {
/* compar s1 i s2 numeric */
double atof(),v1,v2;
v1 = atof(s1);
v2 = atof(s2);
if (v1<v2)
return -1;
else
if (v1>v2)
return 1;
else
return 0;
}
Pentru ca programul nostru s fie complet s mai prezentm i
codul funciei swap, care schimb ntre ei pointerii a dou linii.
swap(char *px[], char *py[]) {
char *temp;
temp = *px;
*px = *py;
*py = temp;
}
__________________________________________________________________________
__________________________________________________________________________
105
106
__________________________________________________________________________
__________________________________________________________________________
107
108
(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.
__________________________________________________________________________
__________________________________________________________________________
109
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,
"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)
__________________________________________________________________________
__________________________________________________________________________
111
112
high = mid - 1;
else
if (cond>0) low = mid + 1;
else
return mid;
}
return -1;
}
/* numr cuvintele cheie */
main() {
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
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
__________________________________________________________________________
__________________________________________________________________________
115
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
Lista-declaraiilor:
declaraie-structur
declaraie-structur, lista-declaraiilor
Declaraie-structur:
specificator-tip, lista-declarator;
Lista-declarator:
declarator-structur
declarator-structur, lista-declarator
n mod obinuit, un declarator-structur este chiar un declarator
pentru un membru al structurii sau reuniunii. Un membru al structurii
poate fi constituit dintr-un numr specificat de bii, caz n care avem
de-a face cu un cmp. Lungimea lui se separ de nume prin caracterul
: Atunci:
Declarator-structur:
declarator
declarator : expresie-constant
: expresie-constant
ntr-o structur fiecare membru care nu este un cmp ncepe la o
adres corespunztoare tipului su. Astfel ntr-o structur pot exista
zone fr nume neutilizate, rezultate din motive de aliniere.
Limbajul C nu introduce restricii privind tipurile obiectelor care
pot fi declarate cmpuri.
Un specificator-structur-sau-reuniune de forma a doua declar
un identificator ca fiind eticheta (marcajul) structurii sau reuniunii.
Atunci o declaraie ulterioar poate folosi forma a treia a unui
specificator-structur-sau-reuniune.
__________________________________________________________________________
__________________________________________________________________________
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
/* pointer la text */
/* numr apariii */
/* descendent stng */
/* descendent drept */
__________________________________________________________________________
__________________________________________________________________________
133
134
__________________________________________________________________________
135
136
__________________________________________________________________________
__________________________________________________________________________
137
138
Valori returnate
n caz de succes se returneaz un pointer de tip FILE. n caz de
eroare se returneaz NULL i variabila global errno indic codul
erorii.
Nume
fflush - foreaz scrierea n flux
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
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.
__________________________________________________________________________
__________________________________________________________________________
139
140
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
fgets - citete un ir de caractere dintr-un flux text
Nume
fread, fwrite - intrri / ieiri pentru fluxuri binare
Declaraie
char *fgets(char *s, int size, FILE *flux);
Declaraie
unsigned fread(void *ptr, unsigned size,
unsigned nel, FILE *flux);
unsigned fwrite(const void *ptr, unsigned
size, unsigned nel, FILE *flux);
Descriere
Funcia fgets cel mult size-1 caractere din flux i le
memoreaz n zona indicat de s. Citirea se oprete la detectarea
sfritului de fiier sau new-line. Dac se citete caracterul new-line
acesta este memorat n s. Dup ultimul caracter se memoreaz null.
Apeluri ale acestei funcii pot fi combinate cu orice apeluri ale
altor funcii de intrare din bibliotec (fscanf, de exemplu) pentru
un acelai flux de intrare.
Valori returnate
Funcia returneaz adresa s n caz de succes, sau NULL n caz de
eroare sau la ntlnirea sfritului de fiier dac nu s-a citit nici un
caracter.
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
__________________________________________________________________________
__________________________________________________________________________
141
142
Conversii
Dup caracterul % care introduce o conversie poate urma un
numr de caractere indicatori, dup cum urmeaz:
*
__________________________________________________________________________
__________________________________________________________________________
143
144
e,g Echivalent cu f.
s
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.
__________________________________________________________________________
__________________________________________________________________________
145
146
Caractere indicatori
Caracterul % este urmat de zero, unul sau mai muli indicatori:
#
Declaraie
int printf(const char *format, ...);
int fprintf(FILE *flux, const char
*format, ...);
int sprintf(char *str, const char *format,
...);
Descriere
Funciile din familia printf genereaz o ieire n concordan cu
format dup cum se descrie mai jos. Funcia printf afieaz ieirea
la fluxul standard stdout; fprintf scrie ieirea la flux;
sprintf scrie ieirea n irul de caractere str.
Aceste funcii genereaz ieirea sub controlul irului format care
specific cum se convertesc argumentele pentru ieire.
irul de formatare
irul format este un ir de caractere, printre care se pot afla zero
sau mai multe directive: caractere obinuite (diferite de %) care snt
copiate aa cum snt n fluxul de ieire, i specificaii de conversie,
fiecare dintre ele rezultnd din ncrcarea a zero sau mai multe
argumente. Fiecare specificaie de conversie este introdus de
caracterul % i se termin cu un specificator de conversie. ntre
acestea pot fi (n aceast ordine) zero sau mai muli indicatori, o
mrime minim a cmpului opional, o precizie opional i un
modificator opional de lungime.
Argumentele trebuie s corespund n ordine cu specificatorii de
conversie. Acestea snt folosite n ordinea dat, unde fiecare caracter
* i fiecare specificator de conversie solicit urmtorul argument.
Dac argumentele nu snt suficiente comportamentul programului
este imprevizibil.
__________________________________________________________________________
__________________________________________________________________________
147
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.
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.
Modificator de lungime
n acest caz prin conversie ntreag nelegem conversie de tip d,
i, o, u, x, X.
h
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
__________________________________________________________________________
__________________________________________________________________________
149
150
Valoare returnat
Funciile returneaz numrul de caractere generate (nu se include
caracterul terminator null pentru sprintf).
Nume
clearerr, feof, ferror - verific i reseteaz starea
fluxului
Declaraie
void clearerr(FILE *flux);
__________________________________________________________________________
__________________________________________________________________________
151
152
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.
__________________________________________________________________________
__________________________________________________________________________
153
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.
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.
__________________________________________________________________________
__________________________________________________________________________
155
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?
#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);
__________________________________________________________________________
__________________________________________________________________________
157
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
isprint
Verific dac c este un caracter afiabil inclusiv spaiu.
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.
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.
echivalent cu
tolower
Convertete caracterul c, dac este o liter, la litera mic
corespunztoare.
isalpha
Verific dac c este alfabetic; este echivalent cu (isupper(c)
|| islower(c)).
toupper
Convertete caracterul c, dac este o liter, la litera mare
corespunztoare.
isalnum
Verific dac c este alfanumeric;
(isalpha(c) || isdigit(c)).
este
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.
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.
__________________________________________________________________________
__________________________________________________________________________
163
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
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
__________________________________________________________________________
__________________________________________________________________________
165
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.
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.
__________________________________________________________________________
__________________________________________________________________________
167
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
*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.
__________________________________________________________________________
__________________________________________________________________________
169
170
double
double
double
double
double
double
double
double
double
double
double
double
double
tg(x)
arcsin(x)
arccos(x)
arctg(x) n [-/2,/2]
double x);
arctg(y/x) n [,]
exp(double x); ex
log(double x); ln(x)
pow(double x, double y);
xy
sinh(double x); sinh(x)
cosh(double x); cosh(x)
tanh(double x); tgh(x)
ldexp(double x, int e);
x 2e
fmod(double x, double y); x modulo y
tan(double x);
asin(double x);
acos(double x);
atan(double x);
atan2(double y,
__________________________________________________________________________
__________________________________________________________________________
171
172
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;
__________________________________________________________________________
__________________________________________________________________________
173
174
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) {
__________________________________________________________________________
__________________________________________________________________________
175
176
char *C,s[11];
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;
}
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
__________________________________________________________________________
__________________________________________________________________________
177
178
Cuprins
5. Declaraii . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
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
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
__________________________________________________________________________
__________________________________________________________________________
179
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