Sunteți pe pagina 1din 113

BORLAND

C
Versiunea 2.0
Manual de utilizare

STRUCTURA GENERAL A UNUI PROGRAM C


1.1. ISTORIC, CONCEPIE, EVOLUIE
Limbajul C a fost finalizat n 1972 de Dennis M. Ritchie i Brian W.
Kernighan de la firma american Bell Laboratories.
Prima versiune a limbajului se numete BCPL apoi urmtoarele poart
numele de A, B i C. Cei doi autori au dezvoltat aceste prime versiuni n
jurul sistemului de operare UNIX.
La vremea respectiv din aproximativ 13000 linii surs ale UNIX-ului
doar 200 de linii surs nu erau scrise n limbajul C. De acest fapt se
leag detractorii limbajului care spun c limbajul C nu este un limbaj
deosebit ci doar un fel de limbaj oficial al sistemului de operare UNIX.
n anul 1978 apare manualul The C Programming Language care este de
fapt i prima standardizare a limbajului. Cei doi autori intr astfel n
istorie...
Dup anul 1980 odat cu dezvoltarea hardware apar i primele PC-uri
iar acestea implic i produse software adecvate. Principalele firme
productoare de sofware -MICROSOFT i BORLAND - au dezvoltat unelte
adecvate pentru programarea i utilizarea limbajului C. Deocamdat firma
BORLAND deine supremaia prin versiunile mediului BORLAND C. Cele mai
folosite sunt versiunile 2.0, 3.1, 4.0. n ultimii doi ani au aprut aa
numitele medii visuale: VISUAL C versiunile 4.5 i 5.0 care sunt de fapt
extensii ale mediului BORLAND C adaptate programrii orientate obiect i
interfeei grafice WINDOWS 95. Mediile de programare BORLANDC pot compila 3
tipuri de programe surs C:

fiiere cu extensia .C (fiiere cu prg. standard C);


fiiere cu extensia .CP (fiiere cu prg. C+,);
fiiere cu extensia .CPP (fiiere cu programe C++).

Menionm c limbajul C++ a fost elaborat de Bjarne Stroustrup de la


AT&T. El este un superset al limbajului C i permite principalele concepte
ale programrii prin abstractizarea datelor i programrii orientate spre
obiecte.
Limbajul C este un limbaj hibrid avnd faciliti caracteristice
limbajelor de asamblare ct i faciliti ale limbajelor de nalt nivel.
Cteva dintre principalele caracteristici ale limbajului C sunt:
portabilitate: chiar dac acest concept nu-i definit foarte riguros
spunem c un program este portabil dac el poate fi transferat uor de
la un tip de calculator la altul; limbajul C este un astfel de limbaj;
flexibilitate: compilatorul face un numr mai redus de controale (face
multe conversii implicite);
programare
structurat:
limbajul
are
principalele
structuri
ale
programrii structurate: structura secvenial, structura iterativ i
structura de selecie;
compactizare: unele instruciuni sunt scrise foarte compact; de exemplu
i:=i+1 se poate scrie mai scurt ca i++;
lucrul pe bii i calcule cu adrese.

CONCEPTUL DE FUNCIE
Un program C se compune din una sau mai multe funcii. Funcia este o
unitate lexical de program compilabil independent.
Una dintre funcii este funcie principal, numele ei este predefinit
i anume main.
Execuia programului ncepe cu prima instruciune din funcia
principal.
Dm n continuare 2 exemple de structuri de program (fiiere surs):

[directive de preprocesare]

[directive de preprocesare]

[declaraii de date globale]

[declaraii de date globale]

[declaraie prototip funcia f1


. . .
declaraie prototip funcia fn]

[declaraie prototip funcia f1


. . .
declaraie prototip funcia fn]

[Void main (void)


{ declaraii
instruciuni instruciuni
}]

[Void main (void)


{ declaraii
instruciuni instruciuni
}]

[Implementare funcia f1]


. . .
[Implementare funcia fn]

[implementare funcia f1]


. . .
[implementare funcia fn]

Funcia principal main este obligatorie pentru orice program


celelalte elemente fiind optionale. Pentru ca o anumit funcie s poat fi
apelat e necesar ca ea s aib declarat prototipul dac implementarea
(definiia) ei se afl dup funcia main (exemplul 1). Dac funcia
principal se afl la sfritul fiierului atunci nu mai e necesar
prototipul funciei apelate ci doar implementarea ei (exemplul 2).
Comparnd structura unui program C cu structura unui program PASCAL se
observ nivelul de imbricare diferit. n PASCAL apare o imbricare a
procedurilor i funciilor pe cnd n C nu exist o astfel de imbricare
(rmne la nivelul fiecrei funcii dac e cazul).

PASCAL C

...

1.2.1. Definiia unei funcii


Definiia unei funcii n limbajul C se compune din antet i corp. O
funcie poate fi apelat dac este precedat de definiia sau de prototipul
ei. Aceste lucruri care sunt valabile n limbajul C se regsesc i n
limbajul C++.
Antet i prototip
Antetul simplificat al unei funcii n C are formatul:
tip nume_funcie (lista_parametrilor_formali)
unde: tip - reprezint tipul valorii returnate de funcie sau dac
funcia nu

returneaz nici o valoare se pune cuvntul cheie void;


nume_funcie reprezint un identificator clasic format dintr-un
mixaj
de litere i cifre, primul caracter fiind obligatoriu litera;
printre litere se numr i liniua de subliniere;
lista_parametrilor_formali nume de variabile separate prin virgule.
Exemple:
1) double radical (double x)

// calculeaza radacina patrata din x si returneaza valoarea

gasita
2) double radical_n (double x, int n)

// calculeaza radacina de ordinul n din x

Prototipul unei funcii este asemntor antetului dar la sfrit se


pune caracterul ;
1.2.3. Corpul unei funcii
Corpul unei funcii C se compune din declaraii de variabile locale
i instruciuni scrise ntre acolade conform figurii de mai jos.
{ declaraii
instruciuni
}
i pentru c autorii limbajului C consider c un limbaj de
programare se nva mai repede scriind i executnd programe ct mai
timpuriu vom da un mic exemplu de funcie.
int modul (int i)

// determina valoarea absoluta a intregului i si returneaza aceasta

valoare
{ if (i < 0)
if (i = = 0)
else
}

return i;
return 0;
return i;

1.3. CONSTRUCIILE DE BAZ ALE LIMBAJULUI


1.3.1. Caractere
Limbajul C folosete setul de caractere al codului ASCII care se
codific prin numere ntregi din intervalul [0,127], adic 128 de coduri.
Un astfel de ntreg se pstreaz pe un BYTE (OCTET) adic pe 8 bii.
Mulimea caracterelor se mparte n trei grupe:
- caractere negrafice (coduri cuprinse ntre 00=NUL i 31) i
127=DEL;
- spaiu (codul 32);
- caractere grafice (coduri cuprinse ntre 33 i 126).
Caracterele grafice se mpart la rndul lor n:
litere mari (coduri ntre 65 i 90);
litere mici (coduri ntre 97 i 122);
cifre (coduri ntre 48 i 59);
caractere speciale (celelalte coduri).
Caracterele negrafice au diferite funcii. Astfel codul 10 semnific
LF (Line Feed), adic deplaseaz cursorul pe linia urmtoare n coloana 1,
iar codul 13 semnific CR (Carriage Return) adic deplaseaz cursorul n
coloana 1 aceeai linie. n limbajul C combinaia celor dou caractere se
noteaz prin:
\n
i are semnificaia trecerii cursorului la linie nou i coloana 1
(newline).
Tabulatorul orizontal (deplasarea cursorului peste un anumit numr de
poziii) se precizeaz prin notaia:

\t
S mai precizm c urmtoarele 3 caractere: spaiu,
tabulator orizontal se mai numesc spaii albe (white spaces).

newline

1.3.2. Nume
n limbajul C, un nume este o succesiune de litere i eventual
cifre, care ncepe cu o liter. Ca lungime un nume poate fi orict de lung
dar numai primele 32 de caractere se iau n considerare. Folosind notaia
BNF (vezi anexa) un nume se poate defini recursiv, ca mai jos:
<nume> ::= <litera> | <nume><litera> | <nume><cifra>
<litera> ::= A|B|C|D|E|F|G|H|I|J|K|L|M|N|O|P|Q|R|S|T|U|V|W|X|Z|
a|b|c|d|e|f|g|h|i|j|k|l|m|n|o|p|q|r|s|t|u|v|w|x|z|_
<cifra> ::= 0|1|2|3|4|5|6|7|8|9|
Numele se folosesc pentru denumirea variabilelor, tablourilor,
funciilor, etc.
Exemple:
A, _start, a_, matrice, matrice_patratica.
Dm i cteva contraxemple:
&B - conine caracterul &;
x+y - conine caracterul +;
1.3.3. Cuvinte cheie
Un cuvnt cheie este un cuvnt mprumutat din limba englez,
care are un neles predefinit. Aceste cuvinte se scriu cu litere mici. Un
cuvnt cheie nu poate avea alt utilizare ntr-un program C dect cea care
i-a fost predefinit. Fiind o succesiune de litere, un cuvnt cheie este un
nume. Lista cuvintelor cheie se d n anexa de la sfritul crii. Sensul
fiecrui cuvnt cheie va rezulta la definirea construciei n care se
utilizeaz.
Exemple:
for, do, if, while, else, break, return, int, long, double,
static, extern.
1.3.4. Tipuri de baz
n limbajul C distingem cteva tipuri predefinte de date care se mai
numesc tipuri de baz. n general un tip de dat are trei atribute bine
conturate:
mulimea valorilor;
reprezentarea (intern i extern)
comportamentul (adic operaiile ce se pot face cu acel tip).
n tabelul de mai jos dm cuvntul cheie, mulimea valorilor,
reprezentarea intern.
Cuvnt cheie Mulimea valorilor

Reprezentarea intern
Lungime (bii)
Formatul intern

int

ntregii din [-215; 215-1]

16

reprezentare

short

ntregii din [-215; 215-1]

16

reprezentare

binar
binar
long ntregii din [-231; 231 - 1]
unsigned
ntregii din [0; 216-1]
char
float
[-3.4*10-38; 3.4*1038]
simpl precizie
double
[-1.7*10-308; 1.7*10308]
dubl precizie

32
reprezentare binar
16
reprezentare binar
cod ASCII
32
virgul
flotant
64

virgul

flotant

Facem precizarea c se poate folosi i combinaia unsigned long


pentru ntregii fr semn n dubl precizie.
Cu tipurile de baz se pot defini i tipuri utilizator folosind
instruciunea struct.
1.3.5. Constante
O constant are un tip i o valoare. Att tipul ct i valoarea
unei constante se definesc prin caracterele care compun constanta
respectiv.
Constant ntreag zecimal
O constant ntreag este un ir de cifre care eventual este
prefixat de un semn (+ sau -) i postfixat de litera L sau l. O constant
ntreag se reprezint n binar pe 16 sau 32 de bii. Sufixul L sau l
foreaz reprezentarea pe 32 de bii.
Constant ntreag octal
O constant ntreag octal este un ir de cifre
cuprinse ntre 0 i 7) precedat de un zero nesemnificativ.

octale

(cifre

Constant ntreag hexazecimal


O constant ntreag hexazecimal este un ir de cifre hexazecimale
prefixat cu 0x sau 0X.
Exemple:
Reprezentare Interpretare
12345
ntreg zecimal reprezentat n binar pe
-12345
ntreg zecimal reprezentat n binar pe
12345L
ntreg zecimal reprezentat n binar pe
012345
ntreg octal reprezentat n binar pe
octal pe 3 bii)
0xabcd
ntreg hexa reprezentat n binar pe 16
bii)
12345678 ntreg zecimal reprezentat pe 32 de bii

16 bii
16 bii
32 bii
16 bii (o cifr
bii (o cifr pe 4

Constant flotant
O constant flotant este un numr raional care se compune din
urmtoarele elemente:
un semn (+ sau -) care poate lipsi pentru numerele pozitive;
o parte ntreag care poate fi i vid;
o parte fracionar care poate fi i vid;
un exponent care poate fi i vid.
Prezena prii fracionare este suficient pentru ca numrul
respectiv s reprezinte o constant flotant. Absena prii fracionare
implic prezena prii ntregi i a exponentului.
Partea ntreag reprezint o succesiune de cifre zecimale.
Partea fracionar se compune din caracterul punct urmat de o succesiune de
cifre zecimale, care poate fi i vid. Exponentul se compune din litera e
sau E urmat de un + sau -, opional, i un ir de cifre zecimale.
Constantele flotante se pstreaz n format flotant dubl
precizie.
Exemple:
3.14; 0.314e1; 3.1415926; 2.71828; 2.; 271828E-5.
Constant caracter
O constant caracter reprezint un caracter i are ca valoare
codul ASCII al caracterului respectiv. O constant caracter grafic se poate
scrie incluznd caracterul respectiv ntre caractere apostrof.

Exemple:
Constant caracter valoare cod ASCII
A
B
a
0
9
*

65
66
97
48
58
77

Anumite caractere negrafice au notaii speciale la scrierea


crora se utilizeaz caracterul backslash (\). Dm n continuare un tabel
cu cele mai folosite caractere negrafice:
Caracter Reprezentare ca i constant caracter Valoare cod ASCII
Backspace
Retur de car
Newline
Apostrof
Backslash
Tabulator vertical
Salt pagin imprimant
Carcterul NUL

\b
\r
\n
\
\\
\v
\f
\0

8
13
10
39
92
11
12
0

Constant ir de caractere
O constant ir de caractere este o succesiune de zero sau mai
multe caractere delimitate prin ghilimele. Ghilimelele nu fac parte din
irul de caractere.
Exemple:
123; Limbajul C; sir de caractere; sir\; (irul vid).
Constanta
ir
de
caractere
se
reprezint
n
memoria
calculatorului printr-o succesiune de octei n care se pstreaz codurile
ASCII ale caracterelor irului, iar ultimul octet conine caracterul NUL
care marcheaz sfritul irului de caractere.
Dup cum se observ din exemplele date putem s folosim i convenia
cu backslash.
De reinut c X i X reprezint construcii diferite.
1.3.6. Variabile simple
Prin variabil nelegem o dat a crei valoare se poate schimba n
timpul execuiei programului care o conine. Unei variabile i se ataeaz
un nume prin intermediul cruia o putem referi sau modifica. Totodat
valorile pe care le poate lua o variabil trebuie s aparin aceluiai
tip. Corespondena dintre numele i tipul unei variabile se realizeaz
printr-o construcie special numit declaraie.
Dup modul de grupare datele sunt:
date izolate denumite i variabile simple;
date grupate:
- mulimi ordonate de date de acelai tip la care ne referim cu
indici,
- structuri n care date de tipuri diferite se grupeaz.
Declaraia de variabil simpl are formatul general:
tip list_de_nume;
unde list_de_nume este format fie dintr-un singur nume fie din mai
multe, separate prin virgule.

n limbajul
utilizate.
Exemple:
int a,b,c;
float x,y,z;
bii;
char car;
long i,j,k;

C trebuie declarate toate variabilele nainte de a fi


// a, b, c sunt variabile de tip int reprezentate in binar pe 2 octei;
// x, y, z sunt variabile de tip float in format flotant simpla precizie pe 32 de
// car este o variabila de tip char pe 1 octeti poate conine coduri ASCII;
// i, j, k sunt variabile de tip long in format binar pe 32 de bii.

Tablouri
Un tablou ca orice variabil simpl trebuie declarat nainte de a fi
utilizat. Dac referirea la elementele tabloului se face cu un singur
indice se spune c tabloul este unidimensional (se mai numete vector);
dac
referirea
se
face
cu
doi
indici
tabloul
se
numete
bidimensional(matrice); iar cu n indici tabloul este n-dimensional.
Declaraia unui tablou n forma cea mai simpl este:
tip nume[l1][l2]...[ln];
unde:
l1, l2, ... ln sunt expresii constante care au valori ntregi ce
pot fi evaluate de compilator la ntlnirea lor.
Evident c se pot declara mai multe tablouri deodat i atunci numele
de tablouri se pot nirui ntr-o list, fiecare separat prin virgul.
Exemple:
int t[5];
float a[5][3];

// s-a declarat un tablou unidimensional de 5 componente;


// s-a declarat un tablou bidimensional de 5*3=15 componente;

La elementele unui tablou ne referim prin variabile cu indici.


O variabil cu indici se compune din numele tabloului urmat de unul sau mai
muli indici, fiecare indice fiind inclus n paranteze drepte. Numrul
indicilor definete dimensiunea tabloului. Indicii sunt expresii care au
valori ntregi. Limita inferioar a indicilor este zero. Astfel dac t este
tabloul de tip int n exemplul de mai sus, elementele lui sunt:
t[0], t[1], t[2], t[3], t[4].
n cazul matricii a declarate mai sus elementele ei vor fi:
prima linie: a[0][0], a[0][1], a[0][2];
a doua linie: a[1][0], a[1][1], a[1][2];
...
a cincea linie: a[4][0], a[4][1], a[4][2];
Deci pentru dimensiunea k indicele variaz ntre 0 i lk-1.
Repartizarea memoriei pentru un tablou se face astfel: pentru
fiecare element al tabloului se repartizeaz o zon de memorie necesar n
conformitate cu tipul tabloului respectiv (pentru tipul int cte 2 octei,
pentru tipul float cte 4 octei, etc). Numele unui tablou este un pointer,
el poate fi utilizat n diferite construcii i el are ca valoare adresa
primului su element.
n desenul urmtor se indic repartizarea memoriei pentru
tabloul t din exemplul de mai nainte:
t
adresa lui
t[0]
t[0] t[1] t[2] t[3] t[4]

Comentariu

n limbajul C un comentariu se scrie ntre /* i */ pe oricte


rnduri. ntre /* i */ se poate scrie o succesiune arbitrar de caractere,
care ns nu poate s conin secvena de terminare (adic */). Comentariul
poate fi inserat ntr-un program n orice poziie n care este legal s
apar un caracter alb. El constituie o explicaie pentru programator sau
pentru utilizator. Compilatorul nu interpreteaz comentariul n nici un
mod. Comentariul se recomand a fi inserat n punctele n care
programatorul poate lmuri prin explicaii, anumite aspecte ale procesului
de calcul sau ale datelor utilizate. n mediile BORLAND C exist i o alt
convenie de a marca un comentariu la nivelul unui rnd cu ajutorul a dou
caractere slash (//), convenie pe care am folosit-o de altfel n exemplele
anterioare.
1.4. PREPROCESARE
Un program surs C poate fi prelucrat nainte de a fi compilat.
O astfel de prelucrare se numete preprocesare sau precompilare. Acest
lucru se realizeaz printr-un program special numit preprocesor. Acesta
este apelat automat nainte de a ncepe compilarea.
Preprocesorul limbajului C realizeaz urmtoarele:
includeri de alte fiiere (de obicei fiiere surs);
definiii i apeluri de macrouri simple;
compilare condiionat.
1.4.1. Includerea unui fiier surs
Fiierele surs pot fi incluse cu ajutorul directivei include.
Dou formate se folosesc pentru a include un fiier surs n locul n
care apare directiva (de obicei se pune la nceputul programului):
#include specificator_de_fisier
sau
#include <specificator_de_fisier>
unde: specificator_de_fiier trebuie s fie un nume de fiier valid
din punct de vedere al sistemului de operare DOS i de obicei are extensia
.h sau .c.
Prima variant este folosit de obicei cnd fiierele de inclus sunt
furnizate de utilizator; dac nu este indicat calea atunci fiierele sunt
cutate n directorul curent i apoi n directoarele setate pentru
directiva include.
A doua variant este folosit pentru ncorporarea unui fiier
standard care se caut n directoarele setate pentru directiva include (de
obicei astfel de fiiere standard sunt livrate n biblioteci ataate
mediului de programare C).
Odat
localizat
fiierul
dintr-o
directiv
include
se
nlocuiete aceasta prin textul fiierului surs. Deci compilatorul nu va
mai ntlni directiva include ci textul fiierului inclus de preprocesor.
Includerile de fiiere se fac de obicei la nceput pentru ca
textele fiierelor surs (date i funcii) s poat fi utilizate n tot
fiierul surs de lucru. De aceea, la nceputul fiierelor surs vom
ntlni mai multe includeri de fiiere standard: stdio.h, stdlib.h, etc.
Textul unui fiier inclus poate la rndul su s conin directiva include.
Fiierul stdio.h (prescurtarea de la standard input output header) conine
printre altele i funciile standard de intrare-ieire printf i scanf.
Fiierele cu extensia .h se mai numesc i fiiere header (fiiere care se
pun la nceputul fiierului surs). Un alt exemplu de fiier header este
iostream.h folosit n mediul BORLAND C care conine funciile cin (console
input) i cout (console output).

1.4.2. Constante simbolice


Cu directiva define se pot defini constante
macrouri. Constantele simbolice se definesc astfel:

simbolice

#define nume succesiune_de_caractere


Preprocesorul substituie nume cu succesiune_de_caractere peste
tot n fiierul surs care urmeaz poziiei directivei define. Dac
succesiune_de_caractere nu ncape pe un rnd atunci se poate continua pe
rndul urmtor scriind caracterul \ la sfritul primului rnd.
Numele nume definit ca mai sus se spune c este o constant
simbolic. Se recomand ca nume s fie scris cu litere majuscule pentru a
scoate n eviden c este o constant simbolic.
Construcia succesiune_de_caractere folosit pentru a defini o
constant simbolic poate la rndul ei s conin alte constante simbolice.
O constant simbolic poate fi redefinit (tot cu define) sau
poate fi anihilat cu undef (#undef nume).
Exemple:
1)

#define PROCENT 10
...
#define PROCENT 15
...
#undef PROCENT

// din acest punct al fisierului sursa se substituie


// PROCENT cu 10
// de aici PROCENT se substituie cu 15
// din acest punct constanta simbolica PROCENT
// isi inceteaza existenta

...
#define DIM 100
// s-au definit doua constante simbolice DIM
#define DOI_PI (2*3.1415926)
// si DOI_PI
...
int vector[DIM];
// DIM va fi inlocuit cu 100
...
x=DOI_PI;
...
LECIA 2.
CLASE DE VARIABILE (DE MEMORIE)
Compilatorul C aloc memorie variabilelor din program de
dimensiune corespunztoare tipului fiecreia.
Memoria se aloc n 2 moduri:
static, repartizat ntr-o zon special asociat programului;
dinamic, repartizat ntr-o zon special numit stiv (se comport
ca o list LIFO).
n funcie de modul cum se aloc memorie, vom distinge mai multe
clase de variabile.
O prim clas de variabile este aceea a variabilelor globale crora
li se aloc memorie pe toat durata execuiei programului i ele pot fi
utilizate n orice funcie a programului. Alt clas de variabile este
clasa variabilelor locale, aceste variabile au o utilizare local la
nivelul unei funcii.
2.1. VARIABILE GLOBALE
O variabil global are o definiie i attea declaraii de
variabil extern cte sunt necesare.
Definiia unei variabile globale coincide sintactic cu o
declaraie obinuit, dar care este scris n afara oricrei funcii a
programului (fiierului surs). Implicit, definiia unei variabile globale
determin ca variabila respectiv s fie definit ncepnd din punctul
scrierii ei i pn la sfritul fiierului surs respectiv. De aceea se

recomand ca definiiile variabilelor globale s fie scrise la nceputul


fiierului surs, pentru a fi accesate n orice funcie a fiierului.
Pentru a utiliza variabilele globale i n alte funcii situate
n alte fiiere surs dect n cel n care sunt definite, ele trebuie
declarate ca externe n funciile respective.
O declaraie de variabil extern coincide cu o declaraie obinuit
doar c ncepe cu cuvntul cheie extern.
Exemplu:
fiierul n care sunt declarate ca variabile globale fiierul n care sunt folosite ca variabile
externe
int i;
float f; void functie(. . .)
void main(void) { extern int i;
{ i = 10; extern double f;
......
f = 3.14; f = f*i;
......
}}
Variabilele i i f sunt declarate n afara funciei main i n afara
oricrei funcii, deci sunt variabile globale. Ele pot fi folosite n toate
funciile din fiierul surs care conine definiiile lor. Pentru a le
utiliza n funcii situate n alte fiiere surs dect n cel n care sunt
definite ele sunt declarate ca externe. Deci variabilele i i f sunt
declarate ca externe n funcia functie din al doilea fiier surs. Cele
dou fiiere surs care pot fi scrise n momente i de persoane diferite se
pot asambla ntr-un singur program cu ajutorul directivei de preprocesare
include.
2.2. VARIABILE LOCALE
Variabilele locale nu sunt valabile n tot programul. Ele au o
utilizare local n dou feluri:
ca i variabile automatice (alocate pe stiv) la nivelul unei
funcii;
ca i variabile statice (alocate n zona programului) la nivel de
fiier (eventual i la nivelul unei funcii).
Variabilele declarate n interiorul unei funcii i care nu sunt
declarate ca externe sunt variabile locale. Lor li se aloc memorie pe
stiv la intrarea n funcia n care sunt declarate. La ieirea din
funcie, stiva se reface la coninutul dinaintea apelului i variabilele
locale pierd alocarea. Deci ori de cte ori se apeleaz o funcie,
variabilele locale acesteia (denumite i variabile automatice) se aloc
(primesc memorie) pe stiv i la ieirea din funcie variabilele locale
sunt terse din stiv.
Variabilele locale pot s nu fie alocate pe stiv, ci ntr-o
zon de memorie destinat acestui scop. O astfel de variabil local se
numete variabil static. O variabil local static se declar printr-o
declaraie obinuit, precedat de cuvntul cheie static. O variabil
static poate fi declarat att n corpul unei funcii ct i n afara
oricrei funcii. n cazul unei variabile statice declarat n interiorul
unei funcii alocarea nu se face pe stiv ci ntr-o zon de memorie
destinat acestui scop, aceasta fiind deosebirea esenial fa de
variabilele automatice. n cazul n care o variabil static este declarat
n afara funciilor, ea este definit din punctul n care a fost declarat
i pn la sfritul fiierului surs care conine declaraia ei. Spre
deosebire de variabilele globale, o variabil static nu poate fi declarat
ca extern i deci nu poate fi utilizat n funciile din alte fiiere
surs diferite de cel n care a fost declarat.
Se recomand ca tablourile mari s fie declarate statice, deoarece
dac sunt automatice pot depi capacitatea stivei (care are implicit 4K
octei).
Exemple:

Fiierul fisier1.c este un fiier surs care conine 2 variabile


globale i i d , o variabil static x i dou funcii f i main. Funcia
main conine variabila static a iar funcia f conine variabila static b.
int i;
double d;
static int x;

// definiia variabilei globale i


// definiia variabilei globale d
// definiia variabilei statice x, locala fisierului fisier1.c

void main (void)


{ static char a;
// definiia variabilei statice a, locala funciei main
float c;
// definiia variabilei automatice c, locala funciei main
/* in acest moment se pot folosi variabilele i,d,x,a si c */
...
}
{ int p;
// definiia variabilei automatice p, locala funciei f
static float b;
// definiia variabilei statice b, locala funciei f
/* in acest moment se pot folosi variabilele i,d,x, p si b */
...
}
Variabilele a i c fiind locale funciei main nu pot fi
utilizate n funcia f. Analog, variabilele p i b sunt locale n funcia
f, nu pot fi utilizate n funcia main.
Fiierul fisier2.c conine funciile f1 i
componena aceluiai program ca i funciile main
fisier1.c

f2
i

care intr n
f din fiierul

static unsigned t;
// definitia variabilei statice t, locala fisierului fisier2.c
void f1(...)
{ extern int i;
// declaratie externa pentru i
extern double d;
// declaratie externa pentru d
static int k;
// definitia variabilei statice k, locala functiei f1
/* in acest moment se pot folosi varibilele i,d, k si t */
...
}
void f2(...)
{ extern int i;
// declaratie externa pentru i
static double s;
// definitia variabilei statice s, locala functiei f2
/* se pot folosi variabilele i, s si t */
...
}
Variabila static x definit n fiierul fisier1.c nu poate fi
utilizat n fiierul fisier2.c. De asemenea, variabila static t nu poate
fi utilizat n fiierul fisier1.c. Variabila global d nu poate fi
utilizat n funcia f2, ea nefiind declarat ca i extern.
Observaii:
1o. Variabilele globale constituie un mijloc simplu de interfa
ntre funciile unui program. Se recomand a fi folosite cnd dorim
transferuri de valori ntre dou sau mai multe funcii n aa fel nct
modificrile realizate de o funcie s fie accesibile pentru toate
funciile programului. Nu trebuie s se fac abuz n utilizarea
variabilelor globale deoarece constituie i o surs potenial de erori,
pentru c accesul unui mare numr de funcii la anumite date globale
conduce deseori la modificri nedorite i greu evideniabile.
2o. Funciile sunt considerate cu atributul implicit extern. Dac
ntr-un program exist mai multe fiiere surs atunci o funcie poate fi
apelat oriunde, bine neles respectnd convenia definirii ei sau a
prototipului ei nainte de a fi folosit. Putem s limitm definind
funciile cu atributul static precednd antetul funciei prin cuvntul

cheie static. Astfel funcia respectiv devine local i deci apelabil


doar n fiierul n care este definit.
2.3. VARIABILE REGISTRU
Limbajul C ofer posibilitatea de a aloca anumite variabile n
regitri microprocesorului. Deoarece regitri constituie un tip de memorie
ultrarapid n anumite cazuri se poate economisi att timp de execuie ct
i memorie. Se pot aloca n regitri numai parametrii funciilor i
variabilele automatice de tip int, char i de tip pointer. O variabil
registru se declar n mod obinuit, precednd declaraia ei prin cuvntul
rezervat register.
Alocarea ntr-un registru a unei variabile se face numai dac
exist un registru disponibil. n caz contrar, variabila registru se aloc
pe stiv exact ca o variabil automatic. Alocarea se face n ordinea
declarrii variabilelor registru.
Exemplu:
void f (register int i)
{ register char c;
register int j;
...
}
Mai nti se aloc parametrul i ntr-un registru, apoi se aloc
c i j n ali doi regitri.
Se recomand alocarea n regitri a variabilelor care au o
utilizare mare, de exemplu a indicilor de tablouri.
2.4. INIIALIZARE
Variabilelor li se pot atribui valori iniiale. Pentru
variabilele globale valorile iniiale se atribuie prin definiiile lor, iar
n cazul celorlalte variabile se pot atribui valori prin declaraiile lor.
Pentru c de multe ori am folosit cuvintele definiia variabilelor sau
declaraiile varibilelor precizm c ele au neles distinct n anumite
contexte. O variabil global se definete o singur dat i se poate
declara ori de cte ori e necesar utilizarea ei n alte fiiere (evident
declarat extern). n cazul celorlalte tipuri de variabile definiiile i
declaraiile coincid. Totodat definiia i declaraia (prototipul) unei
funcii nu coincid.
O variabil simpl se poate iniializa printr-o declaraie de
forma:
tip nume=expresie;
Variabilelor globale i statice li se atribuie valori iniiale
la lansarea programului. Expresia utilizat n cazul acestor variabile
trebuie s fie o expresie constant care s poat fi evaluat de compilator
la ntlnirea ei. Aceasta, deoarece variabilele globale i statice se
iniializeaz prin valori definite la compilare.
Variabilele automatice se iniializeaz la execuie, de fiecare
dat cnd se activeaz funcia n care sunt declarate. Din aceast cauz,
nu mai este necesar ca expresia s fie o expresie constant. Totui, la
ntlnirea ei, trebuie s fie definii operanzii expresiei de iniializare.
Exemplu:
void f(int n)
{ int i=10;
int k=i+n;
...
}
La ntlnirea expresiei i+n sunt deja definii ambii operanzi:

i a fost n prealabil iniializat cu valoarea 10;


n are o valoare care e transferat la apel.
Variabilele globale i statice neiniializate au implicit valoarea
egal cu zero, iar varibilele automatice neiniializate au o valoare
iniial imprevizibil.
Tablourile se pot iniializa printr-o list de expresii incluse
ntre acolade. Astfel un tablou unidimensional se poate iniializa folosind
urmtorul format:
tip nume[n] = { exp1, exp2, . . . expn }
La iniializarea tablourilor se pot utiliza numai expresii
constante. Numrul expresiilor poate fi mai mic dect numrul elementelor
tabloului. Elementele neiniializate au valoarea zero n cazul tablourilor
globale i statice. Dac se iniializeaz fiecare element al tabloului
atunci numrul n al elementelor acestuia nu mai este obligatoriu n
declaraia tabloului respectiv. Deci se poate scrie astfel:
tip nume[ ] = { exp1, exp2, . . . expn};
Numrul elementelor tabloului se consider c este egal cu cel al
expresiilor care realizeaz iniializarea lor.
Pentru un tablou bidimensional vom folosi urmtoarea structur:
tip nume [n][m] = { {exp11, exp12, . . . exp1m}, // pentru linia
nti
{exp21, exp22, . . . exp2m}, // pentru linia a doua
. . .
{expn1, expn2, . . . expnm}, // pentru ultima linie
};
Numrul expresiilor poate fi mai mic dect m n oricare din
acoladele corespunztoare celor n linii ale tabloului bidimensional. n
acest caz se poate omite doar numrul n (al liniilor tablourilor), m fiind
obligatoriu. Formatul de iniializare a tablourilor bidimensionale se
extinde imediat pentru tablouri cu mai multe dimensiuni.
Tablourile de tip caracter pot fi iniializate folosind un
format mai simplu dect cel indicat anterior. Astfel. un tablou de tip
caracter se poate iniializa printr-o declaraie de forma:
char sir[n] = ir_de_caractere;
Evident, marginea superioar n poate fi omis i n acest caz.
Totodat compilatorul pune automat caracterul NUL dup ultimul caracter al
irului utilizat n iniializare.
Exemple:
1) int itab[] = {1,2,3,4,5}

// tabloul de tip intreg are 5 elemente itab[0] = 1,. . . itab[4] = 5

2) int m[3][3] = {{-1,0,1},{-1},{0,1}};

// tabloul are 3 linii si 3 coloane.

Elementele primei linii sunt iniializate astfel:


m[0][0] = 1;
m[0][1] = 0;
m[0][2] = 1;
n linia a doua se iniializeaz numai m[1][0] = -1; Dac tabloul ar
fi declarat global sau static atunci m[1][1] i m[1][2] ar avea valoarea
zero. Altfel ele au o valoare imprevizibil.
Elementele ultimei linii se iniializeaz astfel:
m[2][0] = 0;
m[2][1] = 1;

declaraiile de mai jos sunt identice:


char sir [ ] = {L,I,M,B,A,J,U,L, ,C};
char sir [ ] = {LIMBAJUL C};
LECIA 3.
EXPRESII, OPERANZI, OPERATORI
3.1. EXPRESII
O expresie n limbajul C este format fie dintr-un operand fie din
mai muli operanzi legai ntre ei prin operatori. O expresie are o valoare
i un tip care se determin aplicnd operatorii conform prioritilor i
asociativitii acestora.
n limbajul C operatorii se asociaz de la stnga la dreapta,
exceptnd operatorii unari i de atribuire, care se asociaz de la dreapta
la stnga.. Totodat pot fi folosite parantezele rotunde pentru a impune o
anumit ordine n executarea operaiilor.
3.2. OPERANZI
Un operand n limbajul C poate fi una din urmtoarele elemente:
o constant;
o constant simbolic;
numele unei variabile simple;
numele unui tablou;
numele unei structuri;
numele unei funcii;
referirea la elementul unui tablou (variabil cu indici);
referirea la elementul unei structuri;
apelul unei funcii;
o expresie inclus n paranteze rotunde.
Unele dintre elementele de mai sus nu au fost nc definite, ele se
vor prezenta n leciile viitoare.
Exemple:
9876
- constant ntreag;
x
- variabil simpl;
t[i][3] - variabil cu indici;
0xabcd - constant hexazecimal;
t - nume de tablou;
(expresie) - expresie inclus n paranteze rotunde.
f1 - numele unei funcii
3.3. OPERATORI
Operatorii limbajului C pot fi grupai n mai multe clase, dar oricum
ei pot fi folosii mpreun ntr-o aceeai expresie. Operatorii au ariti
diferite: unari, binari, ternari i totodat o anumit prioritate implicit
care e redat n tabelul de mai jos. Operatorii de aceeai prioritate se
afl trecui n aceeai linie. Liniile tabelulul conin operatorii
limbajului C n ordinea descresctoare a prioritilor. Astfel n prima
linie se afl operatorii de prioritate maxim, iar n ultima linie
operatorul virgul cu prioritatea cea mai mic. Cu excepia operatorilor
., ->,&,*, a parantezelor rotunde (folosite la definiia i apelul
funciilor) i a parantezelor drepte (folosite la variabilele cu indici)
ceilali operatori vor fi explicai n aceast lecie.
( ) [ ] . ->
- (unar) +(unar)
sizeof
* / %
+ << >>
< <= >= >

*(unar)

&(unar)

++

--

(tip)

= = !=
&
^
|
&&
| |
? : (ternar)
= op= op poate fi: *(binar) / % +(binar) (binar) <<
>> & ^ |
,
3.3.1. Operatori aritmetici
Lista operatorilor aritmetici este redat mai jos:
- (minus unar);
+ (plus unar);
* / % operatori binari multiplicativi; (nmulire, mprire, restul
mpririi ntregi);
+ - operatori binari aditivi (adunare i scdere).
Operatorii de pe aceeai linie au aceeai prioritate. Cei unari
au prioritate mai mare dect cei binari. Operatorii multiplicativi au
prioritate mai mare dect cei aditivi.
Exemple:
int i,j,k;
float x,y;
double t[10];
// se dau cateva exemple de expresii folosind operatorii aritmetici
i*x+t[5];
-y+k;
i%j;
// daca i=9 si j=4 atunci i%j are valoarea 1
i/j;
// daca i=9 si j=4 atunci i/j are valoarea 2
x*-y;
// - este operatorul unar deci avem x*(-y)
3.3.2. Operatori relaionali
Lista operatorilor relaionali este redat astfel:
<
(mai mic)
<=
(mai mic sau egal; cele dou caractere ce compun operatorul
sunt concatenate)
>
(mai mare)
>=
(mai mare sau egal; cele dou caractere ce compun operatorul
sunt concatenate)
Toi operatorii relaionali au aceeai prioritate. Ea este mai
mic dect prioritatea operatorilor aditivi. Rezultatul aplicrii unui
operator relaional este 1 sau 0, dup cum operanzii se afl n relaia
definit de operatorul respectiv sau nu.
Exemple:
a= 4 i b= -5
atunci
a>0
are valoarea 1;
a<=0
are valoarea 0;
a+b>0
are valoarea 0;
a>=b
are valoarea 1;
a<0
are valoarea 1;
a+b>=b-a
are valoarea 1;
a+b<=(b-a)*(b-a) are valoarea 0;
3.3.3. Operatori de egalitate
Lista operatorilor de egalitate este redat mai jos:

= = (egal; dou semne = concatenate)


!= (diferit; semnele sunt concatenate).
Operatorii de egalitate au ambii aceeai prioritate i este
imediat mai mic dect a operatorilor relaionali. Operatorul = =
testeaz egalitatea a doi operanzi. Dac operanzii sunt egali atunci
rezultatul operaiei = = este 1, n caz contrar este 0. Operatorul !=
furnizeaz rezultatul 1 cnd cei doi operanzi sunt diferii i 0 cnd sunt
egali.
Exemple:
a= 2 i b=-1
atunci
a= =b
are valoarea 0;
a!=b
are valoarea 1;
a*b!=a+b
are valoarea 1;
3.3.4. Operatori logici
Lista operatorilor logici este redat mai jos:
!
(negaia logic - operator unar);
&&
(I logic);
||
(SAU logic).
Operatorul ! are aceeai prioritate cu operatorii unari +
i -. Operatorul && este mai prioritar dect operatorul ||, dar are o
prioritate mai mic dect operatorii de egalitate.
n limbajul C nu exist valori logice speciale. Valoarea fals
se reprezint prin zero. Orice valoare diferit de zero reprezint valoarea
adevrat.
Dac operatorul ! se aplic la un operand a crui valoare
este zero, atunci rezultatul este 1. Dac acelai operator se aplic la un
operand a crui valoare este diferit de zero, atunci rezultatul este 0.
Dm n continuare tabelele operatorilor logici binari aplicate
valorilor 0 i 1.
&& 0 1
0 0 0 0 0 1 0 0 1
1 0 1 1 1 1 1 1 0

|| 0 1 sau exclusiv 0 1

Chiar dac pentru sau exclusiv nu exist operator el se poate


realiza prin expresia urmtoare aplicat operanzilor a i b: !a&&b||!b&&a
sau folosind parantezele rotunde ((!a) &&b)||((!b)&&a).
Operatorii logici se evalueaz de la stnga la dreapta. Dac la
evaluarea unei expresii se ajunge ntr-un punct n care se cunoate
valoarea ntregii expresii, atunci restul expresiei nu se mai evalueaz.
Dac a=0 i b=1 atunci expresia ! a||b are valoarea 1 pentru c !a
are deja valoarea 1.
3.3.5. Operatori logici pe bii
Lista operatorilor logici pe bii este redat mai jos n ordinea
descrectoare a prioritilor:
~
(operator unar; complement fa de 1)
>> <<
(deplasri la dreapta, respectiv la stnga)
&
(I pe bii)
^
(SAU-EXCLUSIV pe bii)
|
(SAU pe bii)
Operatorul ~, fiind unar, are aceeai prioritate ca i
ceilali operatori unari ai limbajului C. El schimb fiecare bit 1 al
operandului n 0 i invers.

Operatorul >> realizeaz deplasarea


echivalent cu o mprire ntreag cu puteri a
echivalent cu [a/23].
Operatorul << realizeaz deplasarea
echivalent cu o nmulire cu puteri a lui 2; a <<
a*8.

la dreapta care
lui 2; a >> 3

este
este

la stnga care este


3 este echivalent cu

Pentru operatorii &, |, ^ dm n continuare tabelele operaiilor:


& 0 1
0 0 0 0 0 1 0 0 1
1 0 1 1 1 1 1 1 0

| 0 1 ^ 0 1

Observaii:
1o. Operanzii care nu ocup un cuvnt (16 bii) se extind la un
cuvnt. De exemplu expresia ~0 are ca rezultat un cuvnt cu toi bii egali
cu 1.
2o. Operatorii logici pe bii se execut bit cu bit spre deosebire de
operatorii logici care se evalueaz global. De exemplu dac x=2 i y=1 sunt
variabile de tip int atunci:
x&&y
are valoarea 1 pentru c ambii operanzi sunt
diferii de 0.
x&y are valoarea 0 conform schemei de mai jos
0000 0000 0000 0010
0000 0000 0000 0001
&
0000 0000 0000 0000
3o. Operatorul & se folosete frecvent pentru a anula bii din
configuraia unui cuvnt, iar operatorul | pentru a seta (pune) bii ntrun anumit mod;
4o. Operanzii trebuie s fie ntregi (de tipul int sau long).
5o. Atenie la deplasri nu se modific valoarea operandului; deci
trebuie s facem o atribuire; de exemplu a = a << 3 va modifica valoarea
lui a pe cnd a << 3 nu modific valoarea lui a.
Exemple:
Fie declaraia:
int i;
atunci expresia i >> 8 & 255 are ca rezultat valoarea celui mai
semnificativ octet a lui i; i >> 8 deplaseaz octetul mai semnificativ al
lui i n poziia mai puin semnificativ; se face apoi un I logic pe bii
cu masca 255 care pstreaz octetul mai puin semnificativ.
2) Fie expresia: (x >> 6) & ~(~ 0 << 3)
S presupunem c x are valoarea n bii 1010 1011 1000 1101.
Atunci x>>6 are valoarea
1111 1110 1010 1110
Al doilea operand pregtete o masc astfel:
~0
1111 1111 1111 1111
~0<<3
1111 1111 1111 1000
~(~0<<3)
0000 0000 0000 0110
Rezultatul final este
0000
1111
0000

dat de:
0000 0000 0111
1110 1010 1110
0000 0000 0110

Practic s-a obinut valoarea biilor 8,7,6 a lui x (numerotai de la


dreapta la stnga ncepnd cu 0).

3.3.6. Operatori de atribuire


n forma cea mai simpl operatorul de atribuire se noteaz cu
= i se utilizeaz n construcii de forma:
v=expresie;
(v este fie o variabil
element de structur).

simpl,

fie

variabil

cu

indici

sau

un

Aceast construcie se mai numete expresie de atribuire. Ea este


considerat ca fiind un caz particular de expresie. Tipul ei coincide cu
tipul lui v, iar valoarea ntregii expresii este chiar valoarea atribuit
lui v.
O expresie de forma:
v1=(v=expresie);
este i ea legal i se efectueaz n felul urmtor :
se evalueaz expresia expresie i valoarea ei se atribuie lui v;
valoarea lui v se atribuie apoi i lui v1.
Deoarece operatorii de atribuire se asociaz de la dreapta la stnga,
expresia de mai sus se poate scrie i fr paranteze:
v1=v=expresie;
n general, putem realiza atribuiri multiple printr-o expresie
de forma:
vn =. . . =v1=v=expresie
Dac expresia din dreapta semnului egal are un tip diferit de cel al
variabilei v, atunci nti valoarea ei se convertete spre tipul variabilei
v i pe urm se realizeaz atribuirea,
Pentru operaia de atribuire, n afara semnului egal se mai
poate folosi i succesiunea :
op=
unde prin op se nelege unul din operatorii binari aritmetici sau
logici pe bii, adic unul din urmtorii:
% / * - + & ^ | << >>
Acest mod de construcie se folosete pentru a compacta un anumit tip
de atribuire. Astfel expresia:
v op = expresie;
este identic cu expresia de atribuire:
v = op expresie;
Exemple:
int i, j;
double x, y;
int v[10];
i=5;
j=10;
x=y=10.01;
i +=1;
// echivalenta cu i=i+1 si cu i++
x*=3;
// echivalenta cu x=x*3
j<<=10;
// echivalenta cu j=j<<10
v[i]*=i
// echivalenta cu v[i]=v[i]*i
x /= x-y
// echivalenta cu x = x/(x-y)

3.3.7. Operatori de incrementare i decrementare


Aceti operatori sunt unari i au aceeai prioritate cu
ceilali operatori unari ai limbajului C. Operatorul de incrementare se
noteaz prin ++ i mrete valoarea operandului cu unu, iar operatorul de
decrementare se noteaz prin - - i micoreaz valoarea operandului cu
unu. Operatorii sunt folosii prefixat i postfixat. Astfel operatorii
prefixai au notaia:
++operand;
- - operand;
Ei se aplic mai nti i apoi se folosete valoarea lor.
Astfel operatorii postfixai au notaia:
operand++;
operand - -;
Se folosete valoarea operanzilor i apoi se aplic incrementarea sau
decrementarea.
Menionm c aceti operatori se pot aplica numai la urmtorii
operanzi:
variabil simpl;
variabil cu indici;
referire la elementul unei structuri.
Exemple:
int i,j;
double x,y;
int vector [5];
j=i++;
y=--x;
i=++vector[j]

// este echivalent cu j=i si i=i+1;


// este echivalent cu x=x-1 si y=x;
// este echivalent cu vector[j]=vector[j]+1 si i=vector[j]

3.3.8. Operatorul de conversie explicit (expresie cast)


Pentru forarea tipului unui operand se folosete o construcie de
forma:
(tip) operand
Prin aceasta valoarea operandului se convertete spre tipul indicat
n paranteze.
Exemplu:
int i,j;
double y;
i=8; j=5;
y=i/j;
// y are valoarea 1, pentru ca se face impartirea intreaga i/j
Dac vom converti operanzii i i j spre tipul double se va obine
rezultatul corect adic 1.6.
Deci:
int i,j;
double y;
i=8; j=5;
y=(double) i / (double) j;
// y are valoarea 1.6,
Construcia (tip) este un operator unar prin care se expliciteaz
conversia dorit. Are aceeai prioritate ca restul operatorilor unari.
3.3.9. Operatorul dimensiune (sizeof)
Pentru a determina lungimea n octei a unei date se poate
folosi construcia:

sizeof (data)
unde data poate fi:
numele unei variabile simple;
numele unui tablou;
numele unei structuri;
numele unui tip;
referirea la elementul unui tablou sau structur.
Exemple:
int i;
long l;
float f;
double d;
char c;
int itablou[5];
double dtablou[5];
sizeof (i)
sizeof (l)
sizeof (f)
sizeof (d)
sizeof (c)
sizeof (itablou[1])
sizeof (dtablou[1])
sizeof (itablou)
sizeof (dtablou)

// are valoarea 2;
// are valoarea 4;
// are valoarea 4;
// are valoarea 8;
// are valoarea 1;
// are valoarea 2;
// are valoarea 8;
// are valoarea 10;
// are valoarea 40;

Regula conversiilor implicite


n general o expresie C conine operanzi de tipuri diferite.
Pentru operatorii binari exist situaii cnd operanzii nu sunt de acelai
tip i trebuie executate conversii astfel nct operatorii s se aplice
pentru operanzi de acelai tip. Aceste conversii le face automat
compilatorul. Exist o regul a conversiilor implicite care are urmtorii
pai:
fiecare operand de tip char se convertete spre tipul int i fiecare
operand de tipul float se convertete spre double;
dac unul dintre operanzi este de tip double atunci i cellalt se
convertete spre tipul double i rezultatul va avea tipul double;
dac unul dintre operanzi este de tip long, atunci i cellalt se
convertete spre tipul long i rezultatul va avea tipul long;
dac unul dintre operanzi este de tip unsigned, atunci i cellalt se
convertete spre tipul unsigned i rezultatul va fi de tipul unsigned;
la acest pas se ajunge numai dac ambii operanzi sunt de tip int i
deci operaia se execut cu operanzii respectivi, iar rezultatul va fi de
tip int.
Aplicnd regula de mai sus pas cu pas (la fiecare operator n
momentul efecturii lui), se ajunge n final la evaluarea ntregii expresii
i prin acesta se determin tipul expresiei. Regula conversiilor implicite
nu se aplic pentru operatorul de atribuire (valoarea expresiei din partea
dreapt a semnului de atribuire se convertete spre tipul variabilei din
stnga semnului egal).
Exemple:
int i, j, k;
float a, b;
double x, y;
unsigned p;
long r;

char c;
expresii

conversii

i-j/k nu
a/b

tipul expresiei

int
a spre double
b spre double
nu
a spre double

x+y
i+a
i spre double
i-3.14
i+3
i+x
i-c
x+10
p-10
r*5
(double)(i/j)

double
i spre double
nu
i spre double
c spre int
10 spre double
10 spre unsigned
5 spre long
se realizeaz mprirea ntreag ntre
i i j i rezultatul se convertete spre double

double
double

double
int
double
int
double
unsigned
long
double

Dac rezultatul unei operaii depete domeniul de valori ce


corespunde tipului rezultatului, valoarea respectiv se trunchiaz i
rezultatul este eronat.
3.3.11. Operatori condiionali
Operatorii condiionali sunt ? i : i se folosesc mpreun n
construcii de forma:
exp1 ? exp2 : exp3
Evaluarea se face astfel:
se evalueaz expresia exp1;
dac exp1 este diferit de zero, atunci valoarea i tipul expresiei
condiionale sunt egale cu valoarea i tipul expresiei exp 2; altfel cu
expresia exp3.
Exemplu: procesul de determinare a maximului a dou numere a i b
este:
dac a>b

atunci

max=a
altfel

max=b

sfdac
n limbajul C se poate realiza acest proces cu ajutorul
operatorilor condiionali astfel:
max= a>b ? a : b
Dac a>b atunci expresia condiional are valoarea i tipul lui a
altfel expresia condiional are valoarea i tipul lui b.
Operatorul virgul
Operatorul , este folosit pentru gruparea mai multor expresii ntruna singur.
Cu ajutorul acestui operator (care are prioritatea cea mai mic) se
construiesc expresii de forma:
exp1, exp2,. . ., expn
Aceast expresie are valoarea i tipul ultimei expresii (deci a lui
expn).
Exemplu:
k= (i=10, j=j+5; i+j)
Se execut pe rnd cele dou atribuiri de la stnga la dreapta din
parantezele rotunde apoi se face suma i+j i n final se atribuie aceast
sum lui k,

LECIA 4.
INTRRI / IEIRI STANDARD
Limbajul C nu posed instruciuni de intrare / ieire. Aceste
operaii se realizeaz prin apeluri de funcii din bibliotecile standard
ale mediului de programare. De obicei astfel de funcii asigur interfaa
programului cu terminalul de la care s-a lansat, cu imprimanta, etc. Se
numesc intrri standard i ieiri standard intrrile respectiv ieirile de
la terminalul de la care s-a lansat programul. Totodat se presupune c
datele de intrare / ieire sunt organizate n fiiere.
Unui program C i se ataeaz n mod implicit urmtoarele
fiiere:
-

stdin
stdout
stderr
stdprn
stdaux

intrare standard;
ieire standard;
ieire standard pentru erori;
ieire pe imprimant;
intrare / ieire serial.

4.1. FUNCIA STANDARD printf


Funcia printf realizeaz ieiri cu format la ieirea standard
stdout, deci afiare la terminalul la care care s-a lansat programul.
Funcia printf se apeleaz printr-o instruciune cu formatul:
int printf (control, lista_expresii);
unde control este un ir de caractere care conine:
texte de scris;
specificatori
de
format
pentru
datele
care
se
scriu
din
lista_expresii.
lista_expresii conine expresii; valoarea fiecrei expresii se scrie
conform unui specificator de format corespondent din parametrul control.
Parametrul control este inclus ntre ghilimele, iar numrul
specificatorilor
de
format
coincide
cu
numrul
expresiilor
din
lista_expresii. Dac dorim s scriem numai un text atunci parametrul de
control nu conine nici un specificator de format iar lista_expresii este
vid (practic este absent).
Un specificator de format are formatul BNF urmtor:
%[-][d..d][.d..d][l1]l2
Dup cum se observ un specificator de format ncepe ntotdeauna cu
caracterul procent (%). Dup acest caracter poate urma una din
construciile urmtoare:
- un caracter - opional; prezena acestui caracter indic cadrarea
la stnga a datei n cmpul n care se scrie (implicit data se scrie
cadrat la dreapta);
-un ir de cifre zecimale opional, care definete lungimea minim a
cmpului n care se scrie data corespunztoare din lista_expresii; data se
scrie astfel:
n cazul n care necesit o lungime mai mic se scrie cadrat la
dreapta sau stnga (n funcie de absena sau prezena semnului -)
n cazul n care necesit o lungime mai mare se va scrie pe attea
poziii cte i sunt necesare;
-un punct urmat de un ir de cifre zecimale (dup cum se observ
opional); acest element indic precizia datei care se scrie:
dac data se scrie n virgul flotant, precizia definete numrul de
cifre aflate dup marca zecimal (deci cte zecimale);
dac data este un ir de caractere atunci indic cte caractere se
scriu.

-una sau dou litere, care definesc tipul de conversie din formatul
intern n formatul extern:
prima litera poate fi l, ceea ce semnific conversia din formatul
intern long n formatul extern definit de specificator;
a doua liter este obligatorie ntotdeauna i are semnificaiile de
mai jos:
litera

tipul de conversie realizat


d
o
x

pentru
X
pentru

- din int intern n zecimal extern


- din int intern n octal extern
- din int intern n hexazecimal extern (litere mici
cifrele mai mari ca 9 deci a,b,c,d,e,f,)
- din int intern n hexazecimal extern (litere mici
cifrele mai mari ca 9 deci A,B,C,D,E,F)
- din unsigned intern n zecimal extern fr semn
din
binar
intern
(cod
ASCII)
n
caracterul

u
c
corespunztor
s
- din ir de coduri ASCII ntr-un ir de caractere (atenie
ultimul cod este NUL (adic \0)
f
- din float sau double intern n d...d.d...d (implicit 6 cifre
zecimale la partea fracionar dac nu e prezent precizia)
e
- din float sau double intern n d.d...deddd (implicit 6
cifre zecimale la partea fracionar dac nu e prezent
precizia)
E
- din float sau double intern n d.d...dEddd (implicit 6
cifre zecimale la partea fracionar dac nu e prezent
precizia)
g
- se aplic una din conversiile definite de literele f i e
alegndu-se aceea care are un numr minim de poziii
G
- se aplic una din conversiile definite de literele f i E
alegndu-se aceea care are un numr minim de poziii

Literele d, o, x, u pot fi precedate de litera l conversia


realizndu-se din formatul intern long n loc de int.

Observaie.
1o. Dac caracterul % nu este urmat de una din construciile de mai
sus atunci nu reprezint un specificator de format.
Funcia printf ntoarce lungimea total n octei a datelor
scrise la terminal sau valoarea simbolic EOF n caz de eroare. Precizm c
EOF este definit n fiierul header stdio.h astfel:
#define EOF 1.
Totodat funcia printf poate fi testat i astfel:
EOF= = printf (control, lista_expresii)
Dac are valoarea adevrat atunci la scrierea datelor s-a
produs o eroare.
Exemple:
1)
#include<stdio.h>
#include<math.h>
void main(void)
{ int i=10; long j=11;
float a=1.2, b=1.3;
double A=1.4; B=1.5;
clrscr();
// inceputul instructiunilor executabile; se sterge
ecranul
printf ("\ni*j = %d",i*j);
// incep afisarile
printf ("\ni*j = %5d",i*j);
printf ("\ni*j = %-5d",i*j);

printf ("\ni*j = %5.5d",i*j);


printf ("\ni*j = %05d",i*j);
printf ("\na*b = %10.1f",a*b);
printf ("\nA*B = %10.5lf",A*B);
printf ("\nradical(a*b) = %lf",sqrt((double) a*b));
printf ("\nradical(A*B) = %15.10lf",sqrt(A*B));
printf ("\nradical(A*B) = %25.17lf",sqrt(A*B));
printf ("\nradical(A*B) = %25.19lf",sqrt(A*B));
getche();
// asteapta citirea unui caracter de la terminal
}
Rezultatele execuiei programului sunt:
i*j = 110
i*j = 110
i*j = 110
i*j = 00110
i*j = 00110
a*b = 1.6
A*B = 2.10000
radical(a*b) = 1.249000
radical(A*B) = 1.4491376746
radical(A*B) = 1.44913767461894372
radical(A*B) = 1.4491376746189437200
2)
#define sir PC WORLD
void main (void)
{ printf(*%10s*,sir);
printf(*%-10s*,sir);
printf(*%10.5s*,sir);
printf(*%-10.5s*,sir);
}
Rezultatele execuiei programului sunt:

* PC WORLD*
*PC WORLD *
* PC WO*
*PC WO *
4.2. FUNCIA STANDARD scanf
Funcia de bibliotec scanf realizeaz intrri cu format de la
intrarea standard stdin (intrare de la terminalul de la care s-a lansat
programul) i poate fi apelat printr-o instruciune de apel de forma:
scanf (control, lista_adrese);
Ca i n cazul funciei printf, parametrul control este
delimitat de ghilimele i poate conine texte i specificatori de format.
Caracterele albe din parametrul control sunt neglijate. Celelalte caractere
care nu fac parte dintr-un specificator de format, trebuie s existe la
intrare n poziii corespunztoare. Ele se folosesc n scopul realizrii
unor verificri asupra datelor care se citesc.
Un specificator de format ncepe i n acest caz prin
caracterul procent, apoi:
un caracter * opional;
un ir de cifre, opional, care definete lungimea maxim a cmpului
din care se citete data de sub controlul specificatorului de format;
una sau dou litere care definesc tipul conversiei din formatul
extern n formatul intern.
Cmpul controlat de un specificator de format ncepe cu primul
caracter care nu este alb i se termin:
fie la caracterul dup care urmeaz un caracter alb;
fie la caracterul dup care urmeaz un caracter care nu corespunde
specificatorului de format care controleaz acel cmp;
fie la caracterul prin care se ajunge la lungimea maxim indicat n
specificatorul de format.

Condiia c) este absent n definirea cmpului dac n specificatorul


de format nu este indicat lungimea maxim a cmpului.
Literele
care
termin
un
specificator
de
format
sunt
asemntoare cu cele utilizate la funcia printf. n acest caz este
realizat conversia invers fa de cea realizat de funcia printf.
Litera

Tipul conversiei realizate

d
- data din cmpul de intrare este un ir de cifre zecimale,
precedat eventual de un semn i se convertete din zecimal extern n binar
de tip int
o - ca i n cazul literei d, dar din octal extern n binar de tip
int.
x
- ca i n cazul literei d, dar din hexazecimal extern n binar
de tip int; se utilizeaz literele mici a-f sau mari A-F pentru cifrele mai
mari ca 9.
X
- ca i n cazul literei x;
u
- data este un ir de cifre zecimale care formeaz un numr
ntreg fr semn i se convertete din zecimal extern n binar tipul
unsigned.
c
- data se consider format din caracterul curent de la intrare
i parametrului corespunztor i se atribuie codul ASCII al acestui
caracter; n acest caz nu se face avans peste caracterele albe, ca i n
cazul celorlali specificatori.
s
- data se consider c este ir de caractere; irul ncepe,
conform regulii generale, cu primul caracter care nu este alb i se termin
la caracterul dup care urmeaz un caracter alb sau cnd s-au citit attea
caractere cte indic dimensiunea maxim din specificatorul de format;
f
- data de intrare reprezint un numr flotant; deci conversie
din flotant extern n virgul flotant simpl precizie; data care se
citete conine un punct sau un exponent, sau att punct ct i exponent.
Literele d, o, x i u pot fi precedate de litera l i n acest caz
conversia se realizeaz spre long, n loc de int.
De asemenea, litera f va fi precedat de litera l pentru a face
conversii spre formatul virgul flotant dubl precizie.
Lista_adrese conine adresele zonelor receptoare ale datelor citite
prin intermediul funciei scanf. Fiecare dintre parametri reprezint adresa
unei zone receptoare sub forma:
&nume
Ea determin adresa zonei de memorie rezervat variabilei nume.
Caracterul & din construcia de mai sus reprezint un operator unar,
numit operatorul adres. El are aceeai prioritate ca i ceilali operatori
unari din limbajul C.
Exemplu:
void main (void)
{ int i;
long n;
float x;
scanf ( %d %ld %f ,&i,&n,&x); // citeste datele din stdin si le atribuie lui i,n,x.
scanf( %d %*ld %f , &i,&x);
// caracterul * indica faptul ca valoarea pentru
variabila n
// nu se citeste
}
Funcia scanf returneaz numrul de cmpuri citite corect din
fiierul stdin. Dac apare o eroare la citire (din diverse motive de
exemplu neconcordan dintre specificatorul de format i datele din
fiierul stdin) atunci funcia nu va returna numrul total de cmpuri;
citirea se va ntrerupe n locul detectrii erorii i scanf va returna
numrul de cmpuri citite pn n acel moment. Deci de multe ori pentru a

valida formal intrarea datelor (atenie nu e o validare calitativ) se va


folosi o construcie de forma:
nr=scanf(...)
Prin construcia de mai sus se poate pune n eviden sfritul
de fiier, deoarece n acest caz scanf returneaz valoarea EOF. Sfritul
de fiier se poate genera de la tastatur acionnd n acelai timp tastele
CTRL i Z (deci CTRL / Z). Deorece funcia scanf citete datele de la
intrarea standard prin intermediul unei zone de memorie intermediare
(numit zon tampon sau buffer), ea are acces la caracterele din zona
tampon numai dup acionarea tastei ENTER (RETURN). De aceea dup
acionarea combinaiei CTRL / Z se va tasta i ENTER. Totodat aceast
intermediere face posibil eventuale corecturi nainte de a aciona tasta
ENTER.
Exemplu:
Vom citi un ntreg de cinci cifre de la intrarea standard i vom
afia cifrele respective precedate fiecare de dou spaii.
void main (void)
{ int c1,c2,c3,c4,c5;
// c1,c2,c3,c4,c5 sunt cifrele intregului citit
scanf(%1d %1d %1d %1d %1d, &c1,&c2,&c3,&c4,&c5);
// se citesc cifrele intregului in cele 5 variabile cu %1d
printf(%3d%3d%3d%3d%3d,c1,c2,c3,c4,c5);
}
Pentru a citi iruri de caractere se va folosi funcia scanf
fr a mai pune operatorul de adres n faa numelui irului de caractere,
deoarece numele unui tablou reprezint un pointer (deci conine o adres i
anume adresa primului element de tablou).
Exemplu:
Vom citi numele i prenumele unei persoane i le afim pe
ecran.
#define MAX 20
void main(void)
{ char nume[MAX+1], prenume[MAX+1];
//declararea tablourilor de caractere
scanf (%20s %20s,nume, prenume);
//nu s-a mai pus operatorul de adresa
printf (\nnumele: %s, prenumele: %s,nume,prenume);
}
4.3. FUNCIA STANDARD putchar
Funcia standard putchar se poate utiliza pentru a scrie un
caracter n fiierul standard de ieire stdout, n poziia curent a
cursorului. Ea se apeleaz folosind o instruciune de apel de forma:
putchar (expresie);
unde expresie este codul ASCII al caracterului care se scrie la
ieirea standard.
Practic putchar nu este o funcie n sensul definiiei ce am
dat-o n lecia 1, ci este un macrou definit n fiierul header stdio.h,
care folosete o funcie special destinat prelucrrii fiierelor, funcia
putc, astfel:
#define putchar(c) putc(c,stdout)
Exemplu:
void main (void)
{ putchar (A);
// scrie caracterul A in fisierul de iesire in poziia curenta a cursorului
putchar (A+2) // scrie caracterul de cod ASCII A+2=65+2=67, adica litera C
putchar (\n);
// scrie caracterul newline; deci inceput de linie nou
}

4.4. FUNCIA STANDARD getchar


Aceast funcie citete de la intrarea standard, stdin,
caracterul curent i returneaz codul ASCII al caracterului citit. Tipul
valorii returnate este int. La ntlnirea sfritului de fiier (CTRL/Z) se
returneaz valoarea EOF (adic -1). Ca i putchar, getchar este un macrou
definit n fiierul header stdio.h, cu ajutorul unei funcii speciale, getc
destinate prelucrrii fiierelor, astfel:
#define getchar() getc(stdin)
De obicei getchar se folosete n expresii de atribuire de
forma:
c=getchar();
Dup citirea caracterului curent de la intrarea standard se atribuie
variabilei c codul ASCII al caracterului cititi sau EOF la ntlnirea
sfritului de fiier.
Exemple:
1)
#include <stdio.h>
void main (void)
{ putchar(getchar() A + a);

// citeste o litera mare si o rescrie ca litera mica }

2) exemplul urmtor testeaz dac s-a citit o liter mare i numai n


acest caz aplic transformarea ei n liter mic. n cazul n care la
intrare nu se afl o liter mare se rescrie caracterul respectiv.
#include <stdio.h>
void main (void)
{ intc c;
putchar(((c = getchar() )>= A && c<= Z) ? c-A+a :c);
}
4.5. FUNCIILE STANDARD getche I getch
Funcia getche citete de la intrarea standard caracterul
curent i returneaz codul ASCII al caracterului citit. Aceast funcie are
acces direct la caracter dup ce a fost tastat i are forma:
getche();
Funcia getch este similar cu getche cu singura condiie c
citirea se face fr ecou (deci caracterul tastat nu se reafieaz pe
terminal). De fapt prezena/absena sufixului e precizeaz c funcia e sau
nu cu ecou. Cele dou funcii fac citiri fr intermedierea zonei tampon.
LECIA 5.
INSTRUCIUNI
5.1. SCURT ISTORIC AL METODELOR DE PROGRAMARE
Vom prezenta n continuare cteva metode de programare dar nu
exhaustiv, nefiind aici cadrul adecvat (eventual ntr-un curs de Software
Engineering). O clasificare cronologic a ceea ce vrem s prezentm ar fi
urmtoarea:
programarea artizanal;
programarea procedural;
programarea modular;
programarea structurat;
programarea prin abstractizarea datelor;
programarea orientat spre obiecte.
5.1.1. Programare artizanal

Aceast metod de fapt nu este o metod propriu-zis ci este prima


modalitate de programare odat cu apariia calculatoarelor. Intuiia i
experiena programatorului joac un rol important. Fiecare programator i
are propriile reguli de programare. Programele sunt monolitice (un singur
corp de instruciuni), lungi i greu de neles de alt programator. nsui
cel ce a elaborat un astfel de program ntmpin dificulti de nelegere
a propriului program dup un timp oarecare.
5.1.2. Programare procedural
Odat cu apariia primelor limbaje de nalt nivel se utilizeaz
programarea procedural. Necesitatea ca anumite secvene de program s fie
folosite de mai multe ori duce la organizarea acestora n uniti distincte
de program numite n diverse limbaje subprograme, subrutine, proceduri,
etc. De multe ori procedurile trebuie s fie generale deci procesarea s
fac abstractizare de valorile datelor. De exemplu o procedur de calcul al
radicalului de ordinul 2 trebuie s calculeze acest lucru din orice numr
real pozitiv iar pentru cele negative s semnaleze eroare. Procedurile
trebuie deci parametrizate cu anumite variabile numite parametri formali.
Valorile de la apel ale parametrilor formali se numesc parametri efectivi.
Programarea procedural are la baz deci utilizarea procedurilor, iar
acestea realizeaz o abstractizare prin parametri. La apelare o procedur
funcioneaz dup principiul cutiei negre (black box): se cunosc intrrile
i ieirile rezultate din acestea dar nu i modul de transformare care nu e
important n acest moment.
n
majoritatea
limbajelor
procedurale
de
programare
se
consider 2 categorii de proceduri:
proceduri care definesc o valoare de revenire (denumite i funcii);
proceduri care nu definesc o valoare de revenire.
n limbajele C i C++ procedurile de ambele categorii se numesc
funcii.
5.1.3. Programarea modular
Pe msur ce complexitatea aplicaiilor a crescut, a aprut
ideea de a descompune problemele n subprobleme mai simple care la rndul
lor pot fi descompuse n altele mai simple i aa mai departe. n felul
acesta se ajunge la o descompunere arborescent a problemei date n
subprobleme mai simple. Programarea subproblemelor devine o problem mai
simpl i fiecare subproblem are o anumit independen fa de celelalte
subprobleme. De asemenea, interfaa ei cu celelalte subprobleme este
limitat i bine precizat prin procesul de descompunere a problemei
iniiale.
De
obicei,
programarea
unei
subprobleme,
component
a
descompunerii arborescente a problemei iniiale, conduce la realizarea unui
numr relativ mic de proceduri (funcii). Aceste funcii pot prelucra n
comun anumite date. Unele dintre ele sunt independente de funciile
realizate
pentru
alte
subprobleme
componente
ale
descompunerii
arborescente.
Altele
realizeaz
chiar
interfaa
cu
subproblemele
nvecinate.
Despre funciile obinute n urma programrii unei subprobleme
se obinuiete s se spun c sunt nrudite. De obicei, aceste funcii,
mpreun cu datele pe care le prelucreaz, se pstreaz ntr-un fiier i
se compileaz independent.
O colecie de funcii nrudite, mpreun cu datele pe care le
prelucreaz n comun formeaz un modul. n felul acesta, problema iniial
se realizeaz printr-un program alctuit din module.
Programarea modular are la baz elaborarea programelor pe
module.
O parte din datele utilizate n comun de funciile modulului,
sau chiar toate datele modulului, nu sunt necesare i n alte module.
Aceste date pot fi protejate sau cum se mai spune, ascunse n modul.
Limbajul C i C++, permite ascunderea datelor n modul folosind
date care au clasa de memorie static. Mai mult, pot fi declarate i

funciile ca statice i atunci ele vor fi ascunse n modul (nu pot fi


apelate din afara modului). Ascunderea funciilor n modul se face pentru
acele funcii care nu se utilizeaz la realizarea interfeei modulului cu
celelalte module. Ascunderea datelor i funciilor n module permite
protejarea datelor i prentmpin utilizarea eronat a funciilor.
5.1.4. Programarea structurat
Descompunerea unei probleme n subprobleme mai simple se poate
face succesiv n mai multe etape, pn cnd subproblemele sunt direct
programabile sub forma unor proceduri sau module. Aceast descompunere
succesiv se mai numete rafinare pas cu pas (stepwise refinement)..
Evident c se obine o descompunere arborescent. Procedurile se pot
organiza sau nu n module. n cadrul procedurilor se folosesc anumite
structuri de control a execuiei. Aceasta impune o anumit disciplin a
programrii. Structurile de control de sunt:
secvena;
iteraia (pretestat, posttestat, cu numr prestabilit de ciclri);
alternativa (simpl, complet, generalizat).
Instruciunea de baz (primitiv) n cadrul acestor structuri de
control este instruciunea de atribuire.
Aceast abordare a programrii s-a nscut din necesitatea eliminrii
instruciunii de control GO TO care face saltul necondiionat la o
instruciune precizat, alta dect instruciunea urmtoare ei. Profesorul
Dijsktra de la Universitatea din Eindhoven spunea, prin anul 1965,
calitatea unui programator este invers proporional cu numrul de
instruciuni GO TO folosite i a impus D-structurile de control:
secvena;
iteraia pretestat;
alternativa simpl.
D-structurile
se
regsesc
n
toate
limbajele
procedurale.
Corespondena ar fi:
secvena este echivalent cu execuia instruciunilor n ordinea
scrierii lor n programul surs;
b) iteraia pretestat echivalent cu WHILE ... DO;
c) alternativa simpl echivalent cu IF ... THEN.
O

ilustrare

grafic

celor

trei

D-structuri

se

continuare.

da
S1 C
nu da
S2 S C
S nu
.
Sn
S-a demonstrat ulterior (Bohm i Jacopini) c orice algoritm se poate
descrie doar cu D-structurile dar pentru o mai bun lizibilitate i
nelegere a programelor surs s-au adugat i iteraia postestat (REPEAT
... UNTIL), iteraia cu numr prestabilit de ciclri (FOR ... DO),
alternativa complet (IF ... THEN ... ELSE) i alternativa generalizat
(CASE ... OF).
n unele limbaje se folosesc i alte structuri pe lng cele de mai
sus pentru o ct mai fidel reflectare a algoritmului.

5.1.5. Programarea prin abstractizarea datelor


n toate aceste tehnologii anterioare se urmrete mai mult
organizarea programului i mai puin rezolvarea ct mai natural a
problemei. Programarea prin abstractizarea datelor i programarea orientat
spre obiecte propun metodologii n care conceptele deduse din analiza
problemei s poat fi reflectate ct mai fidel n program i s se poat
manevra cu instanieri ale acestor concepte ct mai natural. Se realizeaz
o mai mare fidelitate a programului fa de problem. De exemplu dac ntro problem n care se proceseaz numere complexe e nevoie s se lucreze
ntr-o form ct mai apropiat cu forma matematic se poate introduce tipul
COMPLEX (tip care nu exist n limbajele de programare) i apoi se pot
declara variabile de acest tip. Mai mult ar trebui s se poat face toate
operaiile matematice asupra datelor de tip COMPLEX. n general un TAD (Tip
Abstract de Date) are dou componente fundamentale:
datele membru (reflect reprezentarea tipului);
funciile membru (reflect comportamentul tipului).
5.1.6. Programarea orientat spre obiecte
Un neajuns al programrii prin abstractizarea datelor este
faptul c nu permite exprimarea legturilor dintre diferite concepte (TADuri). Singura legtur dintre concepte care se poate exprima, este aceea c
datele membru ale unei clase pot fi obiecte ale unei alte clase. Acest
lucru nu este suficient n cazul n care conceptele sunt strns dependente
ntre ele formnd structuri ierarhice. Exprimarea ierarhiilor conduce la
atribute suplimentare cum sunt cele de motenire. Aceste atribute conduc la
un nou model de programare pe care l numim programare orientat obiect.
n vrful unei ierarhii se afl fenomenul sau forma de
existen care are trsturi comune pentru toate celelalte componente ale
ierarhiei respective. Pe nivelul urmtor al ierarhiei se afl componentele
care pe lng trsturile comune de pe nivelul superior, mai au i
trsturi suplimentare, specifice. O ierarhie, de obicei, are mai multe
nivele, iar situarea unui element pe un nivel sau altul al ierarhiei este
uneori o problem deosebit de complex. Dm n exemplul urmtor o ierarhie
arborescent pentru conceptele de paralelogram, dreptunghi, romb i ptrat.
Paralelogram

Dreptunghi Romb

Ptrat

Dac paralelogramul se afl n vrful ierarhiei atunci pe nivelul


imediat inferior se aeaz dreptunghiul (paralelogramul cu un unghi drept)
dar i rombul (paralelgramul cu 2 laturi alturate congruente). Apoi
ptratul se poate defini fie ca un dreptunghi cu laturile congruente fie ca
un romb cu un unghi drept. Conceptul de pe fiecare nivel se observ c
motenete proprietile conceptului imediat superior din care este
derivat.
La ora actual, toate ramurile cunoaterii tiinfice sunt
pline de ierarhii rezultate n urma clasificrii cunotinelor acumulate n
perioada lung de observare a fenomenelor i formelor de existen a lumii
materiale i spirituale. Clasificrile ierarhice ale cunotinelor pot fi

ntlnite att n domeniile care pleac de la cele mai concrete forme ale
lumii materiale, cum sunt botanica, zoologia, biologia, etc ct i n
domenii care studiaz concepte dintre cele mai abstracte, cum ar fi
matematica sau filozofia.
Aceste ierarhii sunt rezultatul definirii conceptelor dup regula
includerii genul proxim i diferena specific.
Limbajul C dispune de un set bogat de instruciuni care permit
scrierea de:
programe structurate,
programe flexibile,
programe compacte.
Totodat limbajul C permite aplicarea metodelor de programare
procedural, programare modular i programare structurat. Pe lng aceste
metodologii limbajul C++ permite i programarea prin abstractizarea datelor
i programarea orientat spre obiecte.
Vom descrie n continuare instruciunile limbajului C. Ca o
caracteristic sintactic toate instruciunile limbajului se termin prin
caracterul ;, excepie fcnd instruciunile care se termin cu acolada
nchis.
Limbajul C dispune de un set bogat de instruciuni care permit
scrierea de:
programe structurate,
programe flexibile,
programe compacte.
Totodat limbajul C permite aplicarea metodelor de programare
procedural, programare modular i programare structurat. Pe lng aceste
metodologii limbajul C++ permite i programarea prin abstractizarea datelor
i programarea orientat spre obiecte.
Vom descrie n continuare instruciunile limbajului C. Ca o
caracteristic sintactic toate instruciunile limbajului se termin prin
caracterul ;, excepie fcnd instruciunile care se termin cu acolada
nchis.
5.2. INSTRUCIUNEA VID
Instruciunea vid se reduce la caracterul ;. Ea nu are nici un
efect. Adesea este nevoie de ea la construcii n care se cere prezena
unei instruciuni, dar nu este necesar s se execute nimic n punctul
respectiv.
5.3. INSTRUCIUNEA EXPRESIE
Instruciunea expresie se obine scriind punct i virgul dup
o expresie, deci:
expresie;
Exist cazuri particulare ale instruciunii expresie:
expresia de atribuire, care de altfel este cel mai important caz
particular al instruciunii expresie:
expresie;
apelul unei funcii:
nume_funcie (par1, par2, . . . parn);
incrementrile i decrementrile pre i post fixate:
variabil++; ++variabil;
variabil- -; - - variabil;
Exemplu:
void main (void)

{ int i;
float f;
double d;
i=10;
i++;
f=i;
d=++f;
putchar(a);
}

// instructiune de atribuire
// i se mareste cu 1
// instructiune de atribuire (valoarea lui i se converteste in float)
// incrementare lui f si atribuirea valorii lui d
// instructiune de apel

5.4. INSTRUCIUNEA COMPUS


Instruciunea compus este o succesiune de declaraii urmate de
instruciuni, succesiune inclus ntre acolade:
{
declaraii
instruciuni
}
Pot lipsi declaraiile sau instruciunle dar nu n acelai timp. Dac
delaraiile sunt prezente, atunci ele definesc variabile care sunt valabile
numai n instruciunea compus respectiv.
Exemplu:
...
{ int i=100;
// variabila i este definita in aceasta instructiune compusa
i++;
// i are valabilitate doar intre acolade; dupa acolada inchisa i isi
printf (i=%d\n,i);
// pierde valabilitatea
}
Observaii:
1o. Dup acolada inchis a unei instruciuni compuse nu se pune ;.
2o. Corpul unei funcii are aceeai structur ca i instruciunea
compus, deci o funcie are formatul:
antetul funciei
instruciune compus
5.5. INSTRUCIUNEA if
Instruciunea if permite s realizm o ramificare a execuiei n
funcie de valoarea unei expresii. Ea are dou formate ce permit aplicarea
structurii de alternativ simpl i compus.
Formatul 1:
if (expresie) instructiune;
Efectul:
se evalueaz expresia din paranteze;
dac valoarea expresiei este diferit de zero (deci conform
conveniei are valoarea adevrat), atunci se execut instructiune, altfel
se trece la instruciunea urmtoare.
Formatul 2:
if (expresie)

instructiune_1;
else

instructiune_2;

Efectul:
se evalueaz expresia din paranteze;
dac valoarea expresiei este diferit de zero (deci conform
conveniei are valoarea adevrat), atunci se execut instructiune_1, altfel
se execut instructiune_2; apoi n ambele cazuri se trece la instruciunea
urmtoare.
Observaii:

1o. Se pot folosi instruciuni if imbricate, nivelul de imbricare


fiind oarecare (deci nu exist o limitare a numrului de imbricri).
2o. Pentru mai multe imbricri se folosete regula asocierii if-lui
cu else astfel:
un else se pune n coresponden cu primul if care se afl naintea
lui n textul surs i nu este inclus n instruciunea care l precede pe
el i nici nu i corespunde deja un else.
Exemple
void main (void)
{ float x,y,a;
x=-5;
y=10;
if (x<0)
// ultimul else se asociaza cu primul if iar
if (y<0)
a=1;
// penultimul else se asociaza cu cel de-al doilea if
else
a=2;
else
a=0;
}
void main (void)
{ float x,y,a;
x=-5;
y=10;
if (x<0)
// ultimul else se asociaza cu primul if deoarece cel de-al
{ if (y<0) a=1; }
// de-al doilea if este inclus in instructiunea compusa care
else
a=0;
// il precede pe if
}
void main (void)
// citeste trei intregi si scrie minimul dintre ei
{int i,j,k,min;
scanf (\ndati i=%d, &i);
scanf (\ndati j=%d, &j);
scanf (\ndati k=%d, &k);
if (i>j)
min=j;
else
min=i;
if (k<min)
min=k;
printf (min(%d,%d,%d)= %d\n,i,j,k,,min);
}
5.6. INSTRUCIUNEA while
Instruciunea while are urmtorul format:
while (expresie) instructiune;
Cu ajutorul instruciunii while se realizeaz structura
repetitiv pretestat (condiionat anterior).
Efectul:
se evalueaz valoarea expresiei din paranteze;
dac expresia are valoarea diferit de zero, atunci se execut
instructiune i se reia punctul 1), altfel se trece la instruciunea
urmtoare instruciunii while.
Deci instructiune se execut repetat atta timp ct expresia din
parantez este diferit de zero. Se observ c dac expresia are valoarea
zero de la nceput, atunci instructiune nu se execut niciodat.
Antetul ciclului while este construcia while (expresie) iar
instructiune formeaz corpul ciclului. n cazul n care este necesar s se
execute repetat mai multe instruciuni, se utilizeaz o instruciune
compus format din instruciunile respective.
Exemplu:
Vom crea un program care citete un ntreg n i scrie n!. Algoritmul
n pseudocod este urmtorul:

Citeste n
f=1
i=2
CtTimp i<=n execut
f=f*i;
i=i+1
SfritCtTimp
Scrie n,f
Programul n C este:
#include<stdio.h>
void main (void)
{ int n,i;
double f;
f=1.0;
i=2;
printf(\n dati n= );
scanf(%d,&n);
while (i<=n)
{ f=f*i;
i++;
}
printf(\nn=%d, iar n!=%g\n,n,f);
}
Corpul ciclului while se poate scrie mai compact astfel:
while (i<=n) f*=i++;
5.7. INSTRUCIUNEA for
Instruciunea for, ca i instruciunea while, se utilizeaz pentru a
realiza o structur repetitiv pretestat. Formatul instruciunii este:
for(exp1; exp2; exp3) instructiune;
Antetul ciclului este definit de for(exp1; exp2; exp3) iar
instructiune formeaz corpul ciclului. Prima expresie exp1 constituie
partea de iniializare a ciclului, iar exp3 este partea de reiniializare a
ciclului. Condiia de continuare a ciclului este exp2. De obicei exp1 i
exp3 reprezint atribuiri.
Efectul:
se execut secvena de iniializare definit de expresia exp1;
se evalueaz exp2; dac exp2 are valoarea zero, atunci se iese din
ciclu, adic se trece la instruciunea urmtoare instruciunii for, altfel
se execut instruciunea din corpul ciclului;
se execut apoi secvena de reiniializare definit de exp 3, apoi se
reia secvena de la punctul 2).
Observaii:
1o. Ca i n cazul instruciunii while, instruciunea din corpul
ciclului for poate s nu se execute niciodat dac exp2 are valoarea zero
chiar la prima evaluare.
2o. Expresiile din antetul instruciunii for pot fi i vide; totui
caracterele ; vor fi ntotdeauna prezente.
3o. Comparnd instruciunile for i while observm c instruciunea
for cu formatul anterior se poate realiza cu secvena urmtoare folosind
while:
exp1;
while (exp2)
{ instructiune;
exp3;

}
Invers, o instruciune while de forma: while (exp) instructiune este
echivalent cu urmtoarea instruciune for:
for(; exp; ) instructiune.
Autorii limbajului C propun ca instruciunea for s se foloseasc cu
prioritate pentru ciclurile care au pas.
Exemple:
Vom da o secven de instruciuni care nsumeaz elementele unui
tablou:
s=0;
for(i=0; i<n; i++) s=s+tab[i];
sau scris mai compact:
for (s=0, i=0; i<n; i++) s+=tab[i];
n continuare vom da un mic program
caracterelor citite de la intrarea standard stdin.

care

afieaz

numrul

#include <stdio.h>
void main(void)
{ long n;
for (n=0; getchar()!=EOF; n++);
printf (\nnumarul caracterelor citite =%ld\n,n);
}
sau scris cu instruciunea while
#include <stdio.h>
void main(void)
{ long n=0;
while (getchar()!=EOF) n++;
printf (\nnumarul caracterelor citite =%ld\n,n);
}
5.8. INSTRUCIUNEA do-while
Aceast
instruciune
realizeaz
structura
repetitiv
condiionat posterior (posttestat) dar modificat fa de REPEAT ..
UNTIL. Aceast instruciune s-a introdus pentru o mai mare flexibilitate n
scrierea programelor. Formatul ei este:
do
instructiune;
while (exp);
Efectul:
se execut instruciunea instructiune;
se evalueaz expresia exp din paranteze;
dac valoarea expresiei este zero se trece la instruciunea urmtoare
instruciunii
do-while;
altfel
se
revine
i
se
execut
din
nou
instructiune.
Observaii:
1o. Structura realizat de instruciunea do-while poate fi realizat
printr-o secven n care se folosete instruciunea while astfel:
instructiune;
while (exp) instructiune;
2o. Se observ c n cazul instruciunii do-while, corpul ciclului se
execut cel puin odat, spre deosebire de ciclurile while i for unde
corpul ciclului poate s nu se execute niciodat.
Exemplu:
Vom da un program care calculeaz rdcina ptrat dintr-un numr
real a>=0.
#include<stdio.h>
#define EPS 1e-10
void main (void)
{ double x1,x2,y,a;

clrscr();
// sterge ecranul
printf(\ndati un numar real pozitiv a=);
if (scanf(%lf,&a) !=1 || a<0)
printf (numarul citit nu este pozitiv\n);
else {
x2 = 1.0;
do {
x1 = x2;
x2 = 0.5 *(x1+a/x1);
// formula de iteratie
if ((y=x2-x1) < 0) y = -y;
}
while (y >= EPS);
printf (radacina patrata din:%g este: %.2lf\n,a,x2);
// 2 zecimale
} //sfirsit else
}
5.9. INSTRUCTIUNEA switch
Instruciunea switch permite realizarea structurii alternativa
generalizat. Ea este echivalent cu o imbricare de structuri de
alternativ simple. Utilizarea instruciunii switch face n schimb
programul mult mai clar.
Formatul instruciunii switch este urmtorul:
switch (exp)
{ case c1: sir1
break;
case c2:

sir2
break;
. . .
case cn:
default:

sirn
break;
sir

}
unde:

c1,. . . cn sunt constante sau constante simbolice;


sir1, . . . ,sirn, sir sunt iruri de instruciuni.
Efectul:
se evalueaz expresia din parantez;
se compar pe rnd valoarea expresiei cu valorile constantelor c 1, .
. . , cn;
dac valoarea expresiei coincide cu valoarea lui c k, se execut
secvena de instruciuni definit prin sirk; n cazul n care valoarea
expresiei nu coincide cu nici una din constantele c1, . . . , cn, se execut
secvena de instruciuni definit prin sir;
dup execuia secvenei sirk sau sir se trece la instruciunea
urmtoare instruciunii switch, adic la prima instruciune aflat dup
acolada nchis care termin instruciunea switch respectiv; evident,
acest lucru are loc dac irul care se execut nu impune, el insui, un alt
mod de continuare a execuiei, de exemplu o revenire din funcia
respectiv, un salt la o anumit instruciune, etc.
Observaii:
1o. Ramura default nu este obligatorie. n lipsa ei, dac valoarea
expresiei nu coincide cu nici una din constantele c1,. . . , cn,
instruciunea switch respectiv nu are nici un efect.
2o.Construcia break reprezint o instruciune. Ea termin fiecare
ramur de instruciuni sir1, . . . , sirn, provocnd saltul la instruciunea
urmtoare instruciunii switch sau, cum se mai spune, realizeaz ieirea
din instruciunea switch.
3o. Instruciunea break nu este obligatorie. n cazul n care este
absent, se execut secvenial urmtoarea ramur. De exemplu dac avem
secvena:
switch (exp)
{ case c1: sir1
case c2: sir2
}

ea se execut n felul urmtor:


dac valoarea expresiei este egal cu c1 se execut sir1 i apoi sir2;
dac valoarea expresiei este egal cu c2 se execut sir2;
daca valoarea expresiei difera de valorile c1 i c2 instruciunea
switch de mai sus nu este efectiv, se trece la instruciunea urmtoare
care urmeaz dup switch.
secvena de mai sus se putea realiza i astfel:
if (exp = = c1)
{ sir1
sir2
}else if (exp = = c2) sir2
Exemplu:
Vom citi din fiierul de intrare construcii de forma: op 1 operator
op2, unde op1 i op2 sunt numere ntregi (operanzi ntregi) iar operator
este un operator aritmetic {+, -, *, /}. La ieire se va scrie
valoarea expresiei citite. De exemplu dac se citete secvena 100/3 se va
afia rezultatul 33. Programul permite citirea i evaluarea mai multor
astfel de expresii, pn la ntlnirea sfritului de fiier.
#include <stdio.h>
void main (void)
{ int op1,op2,operator,rezultat,i;
while (( i=scanf(%d %c %d, &op1,&operator, &op2)) != EOF)
if (i = = 3 )
// ramura adevarat inseamna ca s-au citit 3 campuri corecte
{ switch (operator)
{ case +: rezultat = op1 + op2 ;
// avem adunare
break;
case - : rezultat = op1 op2; // avem scadere
break;
case * : rezultat = op1 * op2;
// avem inmultire
break;
case / :
// avem impartire intreaga
if (op2 = = 0)
{ printf (divizor nul\n);
rezultat = 0;
} else rezultat = op1 / op2;
break;
default : printf (operator eronat\n);
rezultat = 0;
} // sfarsit switch
printf (%d %c %d %d\n, op1, operator, op2, rezultat);
} else
printf (expresie eronat\n);
// sfarsit if si while
}
5.10. INSTRUCIUNEA break
Formatul instruciunii este urmtorul:
break;
De obicei instruciunea break se folosete pentru a iei dintr-un
ciclu. Dac exist mai multe cicluri imbricate instruciunea break va trece
controlul la ciclul de nivel imediat superior (deci imbricarea rmne, nu
se iese din toate ciclurile). O alt utilizare este n instruciunea
switch, dup cum am observat n paragraful anterior.
Un alt exemplu de utilizare frecvent este ieirea dintr-un
ciclu infinit de forma:
for ( ; ; )
{. . .
if (exp) break;
. . .

}
5.11. INSTRUCIUNEA continue
Formatul instruciunii este urmtorul:
continue;
Efectul:
n ciclurile while i do-while ea realizeaz saltul la evaluarea
expresiei care decide asupra continurii ciclului;
n ciclul for ea realizeaz saltul la pasul de reiniializare.
Observaie:
1o. Instruciunea continue se utilizeaz numai n corpul unui ciclu,
permind, dup caz, s se treac la pasul urmtor al ciclului sau s se
ias din ciclu.
5.12. INSTRUCIUNEA goto
Conform principiilor programrii structurate instruciunea goto
nu ar fi necesar. Dar ea a fost introdus n limbaj, deoarece, n anumite
cazuri, se dovedete a fi util, asigurnd o flexibilitate mai mare n
programare. De multe ori ieirea dintr-un ciclu imbricat n alte cicluri se
realizeaz mai simplu cu ajutorul instruciunii goto. n lipsa ei ar trebui
s folosim mai muli indicatori i teste asupra valorilor acestora pentru
ieirea din ciclu. Saltul fcut de goto se face la o instruciune care este
prefixat de o etichet.
Prin etichet vom nelege un nume urmat de caracterul :.
Etichetele sunt locale unei funcii.
Instruciunea goto are urmtorul format:
goto eticheta;
Efectul:
se realizeaz saltul la instruciunea prefixat de eticheta al crei
nume se afl scris dup cuvntul cheie goto.
5.13. APELUL I REVENIREA DINTR-O FUNCIE
5.13.1. Apelul unei funcii
n limbajul C funciile sunt de dou tipuri:
funcii care returneaz o valoare la revenirea din ele;
funcii care nu returneaz nici o valoare la revenirea din ele.
O funcie care nu returneaz nici o valoare la revenirea din ea se
apeleaz printr-o instruciune de apel. Ea are urmtorul format:
nume (lista_parametrilor_efectivi);
(*)
unde:
nume este numele funciei;
lista_parametrilor_efectivi este fie vid, fie se compune din una sau
mai multe expresii separate prin virgul.
Instruciunea de apel este un caz particular al instruciunii
expresie. Parametrii efectivi (de la apel) trebuie s corespund cu cei
formali (de la definirea funciei) prin ordine, tip i numr.
n cazul n care o funcie returneaz o valoare, ea poate fi apelat
fie printr-o instruciune de apel, fie sub forma unui operand al unei
expresii.
Observaii:
1o. Dac nu dorim s utilizm valoarea returnat
respectiv, apelul se face printr-o instruciune de apel.

de

funcia

2o. Dac dorim s utilizm valoarea returnat de funcie, vom folosi


apelul funciei drept operand ntr-o expresie, operandul avnd formatul
(*).
Exemple de apeluri de funcii folosite pn acum sunt apelurile
funciilor standard printf, scanf, getchar i putchar. Funciile printf i
putchar au fost apelate prin instruciuni de apel, valorile returnate de
ele nefiind utilizate. n schimb funciile scanf i getchar au fost apelate
n ambele moduri, att prin instruciuni de apel, ct i ca operanzi n
diferite expresii.
5.13.2. Prototipul unei funcii
O funcie poate fi apelat dac ea este definit n fiierul
surs nainte de a fi apelat. Dup cum am prezentat n lecia 1 nu
ntotdeauna este posibil acest lucru i n astfel de cazuri apelul funciei
trebuie s fie precedat de prototipul ei.
Prototipul unei funcii are ca scop s informeze compilatorul
despre:
tipul valorii returnate de funcie;
tipurile parametrilor.
n felul acesta, la apelul unei funcii, compilatorul poate face
teste cu privire la tipul expresiilor care reprezint parametrii efectivi,
precum i unele conversii necesare asupra valorii returnate de funcie.
Observaii:
1o.
Tipurile
parametrilor
pot
s
lipseasc.
n
acest
caz,
compilatorul nu controleaz tipurile parametrilor efectivi, singura
informaie coninut de prototip fiind tipul valorii returnate de funcia
respectiv.
2o. Absena att a prototipului unei funcii, ct i a definiiei
funciei nainte de a fi apelat este posibil; n acest caz se presupune
c funcia returneaz o valoare de tip int.
3o. n practic se recomand utilizarea prototipurilor pentru toate
funciile nainte de a fi apelate. n acest scop, ele vor fi scrise la
nceputul fiierelor surs.
Formatele posibile ale unui prototip sunt:
formatul
formatul
formatul
formatul

1:
2:
3:
4:

tip
tip
tip
tip

nume
nume
nume
nume

(lista_declaratiilor_de_parametri);
(lista_ tipurilor_parametrilor);
(void);
();

Formatul 2 este cel mai utilizat. Formatul 3 se poate folosi


pentru orice funcie care nu are parametri. Formatul 4 se poate folosi
pentru orice funcie la al crei apel nu se doresc teste referitoare la
tipul parametrilor efectivi.
Funciile
din
biblioteca
standard
a
limbajului
C
au
prototipurile definite n fiierele de tip .h.
5.13.3. Apel prin valoare i apel prin referin
La apelul unei funcii, fiecrui parametru formal i se atribuie
valoarea parametrului efectiv care-i corespunde. Deci, la apelul unei
funcii se transfer valorile parametrilor efectivi. Din aceast cauz se
spune c apelul este prin valoare (call by value). n anumite limbaje de
programare, la apel nu se transfer valorile parametrilor efectivi ci
adresele acestora. n acest caz se spune c apelul este prin referin
(call by refference).
ntre cele dou tipuri de apeluri exist o diferen esenial
i anume: n cazul apelului prin valoare funcia apelat nu poate modifica
parametrii efectivi din funcia apelant, neavnd acces la ei. n cazul
apelului
prin
referin,
funcia
apelat,
dispunnd
de
adresele
parametrilor efectivi, i poate modifica.

n limbajul C apelul se realizeaz implicit prin valoare. n


cazul c un parametru este numele unui tablou atunci transferul se
realizeaz prin referin deoarece numele unui tablou este un pointer i
conine adresa primului element al tabloului. Transferul prin referin se
realizeaz cu ajutorul variabilelor de tip pointer i cu ajutorul
operatorului de adres (&).

5.13.4. Revenirea dintr-o funcie


Revenirea dintr-o funcie se poate face n dou moduri:
la ntlnirea instruciunii return;
dup execuia ultimei sale instruciuni, adic a instruciunii care
precede acolada nchis ce termin corpul funciei respective.
Instruciunea return are dou formate:
return;
sau
return expresie;
Primul format se utilizeaz
cnd funcia nu returneaz o
valoare, iar cel de-al doilea cnd funcia returneaz o valoare. n acest
ultim caz, funcia returneaz valoarea expresiei specificate.
Observaie:
1o. Cnd revenirea se face dup execuia ultimei instruciuni a
funciei nu se returneaz o valoare; revenirea n acest caz, se face ca i
cum acolada nchis de la sfritul corpului funciei ar fi precedat de
instruciunea return.
Exemplu: vom da un exemplu de apel
rdacina ptratic dintr-un numr nenegativ.
#include<stdio.h>
double radacina_2 (double)

al

funciei

care

determin

// prototipul functiei

void main (void)


// functia principala care citeste d
// si afiseaza radacina patrata din d
{ double d;
clrscr();
// sterge ecranul
if (scanf (%lf,&d) != || d<0)
printf (numarul dat este eronat\n);
else
printf (d=%f, radacina patrata = %.10g\n, d, radacina_2(d));
#define EPS 1e-10
double radacina_2 (double x)
{ double x1,x2,y;
x2 = 1.0;
do { x1 = x2;
x2 = 0.5 *(x1+x/x1);
// formula de iteratie
if ((y=x2-x1) < 0) y = -y;
}
while (y >= EPS);
return x2;
}
Observaie:
1o. Limbajul C dispune de o bibliotec matematic n care sunt
incluse o serie de funcii pentru calculul valorilor funciilor elementare.
Exist o funcie numit sqrt (cu prototipul double sqrt (double);).
Fiierul care conine biblioteca matematic se numete math.h i trebuie
inclus n fiierul surs de lucru dac se dorete utilizarea funciilor
definite n el.
LECIA 6.
POINTERI

Un pointer este o variabil care are ca valori adrese.


Pointerii se folosesc pentru a face referire la date cunoscute prin
adresele lor. Astfel, dac p este o variabil de tip pointer care are ca
valoare adresa zonei de memorie alocat pentru variabila ntreag x atunci
construcia *p reprezint chiar valoarea variabilei x.
n construcia de mai sus, *p, caracterul * se consider ca
fiind un operator unar care furnizeaz valoarea din zona de memorie a crei
adres este coninut n p. Operatorul unar * are aceeai prioritate ca i
ceilali operatori unari din limbajul C.
Dac p conine adresa zonei de memorie alocat variabilei x,
vom spune c p pointeaz spre x sau c p conine adresa lui x.
Pentru a atribui unui pointer adresa unei variabile, putem
folosi operatorul unar &. Astfel, dac dorim ca p s pointeze spre x, putem
utiliza construcia:
p = &x;
n limba romn se utilizeaz i alte denumiri pentru noiunea
de pointer: referin, localizator; reper; indicator de adres.
6.1. DECLARAIA DE POINTER
Un pointer se declar ca orice variabil cu deosebirea c
numele pointerului este precedat de caracterul *. Astfel, dac, de exemplu,
dorim s declarm variabila p utilizat anterior pentru a pstra adresa
variabilei ntregi x, vom folosi declaraia urmtoare:
int *p;
Tipul int stabilete n acest caz faptul c p conine adrese de
zone de memorie alocate datelor de tip int. Declaraia lui p se poate
interpreta n felul urmtor: *p reprezint coninutul zonei de memorie spre
care pointeaz p, iar acest coninut are tipul int.
n general, un pointer se declar prin:
tip *nume;
ceea ce nseamn c nume este un pointer care pointeaz spre o zon
de memorie ce conine o dat de tipul tip.
Comparnd declaraia de pointer anterioar cu una obinuit:
tip nume;
putem considera c:
tip *
dintr-o declaraie de pointer
obinuit. De aceea, construcia
tip *

reprezint

tip

dintr-o

declaraie

se spune c reprezint un tip nou, tipul pointer.


Exist cazuri n care dorim ca un pointer s fie utilizat cu
mai multe tipuri de date. n acest caz, la declararea lui nu dorim s
specificm un tip anume. Aceasta se realizeaz folosind cuvntul cheie
void:
void *nume;
Exemple:
1)
void main (void)
{ int x,y;
int *p;
y=x+10;
// aceast atribuire este echivalenta cu secventa urmatoare
p=&x;
y=*p+100;

x=y;
p=&x;
(*p)++;
}

// este echivalenta cu secventa

2) funcia permutare de mai jos realizeaz transferul parametrilor


prin adres:
void permutare (int *x, int *y)
// x si y sunt pointeri
{ int temp;
temp = *x;
// temp ia valoarea ce se afla la adresa continuta in x
*x=*y;
// in zona a carei adresa se afla in x se transfera continutul
// zonei a carei adresa se afla in y
*y=temp;
// in zona a carei adresa se afla in y se transfera valoarea
// lui temp
}
Apelul funciei permutare se face astfel:
permutare (&a, &b);
pentru a schimba valorile lui a cu b.

6.2. LEGTURA DINTRE POINTERI I TABLOURI


Numele unui tablou este un pointer deoarece el are ca valoare
adresa primului su element. Totui exist o diferen ntre numele unui
tablou i o variabil de tip pointer, i anume unui nume de tablou nu i se
poate atribui alt adres. Deci numele unui tablou trebuie considerat ca
fiind un pointer constant.
Dac x este un parametru formal ce corespunde unui parametru
efectiv care este un nume de tablou, x poate fi declarat fie ca tablou fie
ca pointer spre tipul tabloului.
Exemplu:
Fie funcia cu antetul urmtor:
unsigned lungime (char x[ ]);
S presupunem c aceast funcie determin lungimea unui ir de
caractere i se poate apela prin:
l=lungime(tablou);
unde tablou este de tip caracter.
Antetul funciei lungime poate fi schimbat n felul urmtor:
unsigned lungime (char *x);
Cele dou declaraii sunt identice deoarece declaraia:
char x[ ];
definete pe x ca numele unui tablou de tip caracter; dar atunci el
este un pointer spre caractere deci se poate declara prin:
char *x;
6.3. OPERAII CU POINTERI
Asupra pointerilor se pot face diferite operaii. Deoarece ei
conin adrese atunci operaiile se realizeaz cu adrese.
6.3.1. Incrementare i decrementare
Operatorii de incrementare
variabilelor de tip pointer.
Efectul:

decrementare

se

pot

aplica

operatorul de incrementare aplicat asupra unui operand de tip pointer


spre tipul tip mrete adresa coninut de operand cu numrul de octei
necesari pentru a pstra o dat de tipul tip.
operatorul de decrementare se execut n mod analog, cu singura
diferen c n loc s se mreasc adresa, ea se micoreaz cu numrul
corespunztor de octei.
De obicei decrementrile i incrementrile adreselor sunt mai rapide
ca execuie cnd se au n vedere prelucrri de tablouri.
Exemplu:
int tab[10];
int *p;
int i=0;
p=&tab[i];
p++;
// p contine adresa lui tab[1]
// cu p se pot face referiri la orice element
de tablou
6.3.2. Adunarea i scderea unui ntreg dintr-un pointer
Dac p este un pointer, sunt corecte expresiile de forma:
p+n i p-n
unde n este de tip ntreg.
Efectul:
expresia p+n mrete valoarea lui p cu n*nr_tip, unde nr_tip este
numrul de octei necesari pentru a memora o dat de tipul tip spre care
pointeaz p;
analog expresia p-n micoreaz valoarea lui p cu n*nr_tip.
Dac x este un tablou de tipul tip, atunci x este pointer, deci o
expresie de forma:
x+n;
este corect i deoarece x este un pointer spre primul su element
x[0], x+n va fi un pointer spre elementul x[n]. Rezult c valoarea
elementului x[n] se poate reprezenta prin expresia:
*(x+n);
Astfel variabilele cu indici se pot nlocui prin expresii cu
pointeri. Aceasta permite ca la tratarea tablourilor s se foloseasc
expresii cu pointeri n locul variabilelor cu indici. Versiunile cu
pointeri sunt de obicei optime n raport cu cele realizate prin intermediul
indicilor.
6.3.3. Compararea a doi pointeri
Doi pointeri care pointeaz spre elementele aceluiai tablou
pot fi comparai folosind operatorii de relaie i de egalitate. Astfel,
dac p i q sunt doi pointeri care pointeaz spre elementele tab[i],
respectiv tab[j] ale tabloului tab, expresiile urmtoare au sens:
p<q
p!=j p= =q.
Observaii:
1o. Pointerii nu pot fi comparai dect n condiiile amintite mai
sus (deci dac sunt pointeri spre elementele aceluiai tablou).
2o. Operatorii = = i != permit compararea unui pointer i cu o
constant simbolic special avnd numele NULL. Aceste comparaii permit s
stabilim dac un pointer conine o adres sau nu. Astfel, dac expresia:
p= = NULL
este adevrat, p nu conine o adres. Dac expresia respectiv are
valoarea fals atunci p conine o adres. Constanta simbolic NULL este
definit n fiierul stdio.h

.
6.3.4. Diferena a doi pointeri
Doi pointeri care pointeaz spre elementele aceluiai tablou pot fi
sczui. Rezultatul diferenei a doi pointeri este definit astfel: fie t un
tablou de un tip oarecare i p i q doi pointeri, p conine adresa
elementului t[i] iar q conine adresa elementului t[i+n]. Atunci diferena
q-p are valoarea n.
6.3.5. Exemple
Vom da cteva funcii asupra irurilor de caractere:
funcia lungime
unsigned lungime (char*x)
{ int i;
for (i=0; *x != \0; i++) x++;
return i;
}

// sau for (i=0; *x++; i++);

funcia copiaz
void copiaza(char *x, char *y)

// copiaza din zona de adresa y


// in zona de adresa x

{ while(*x++ = = *y++); }
funcia concateneaza
void concateneaza (char *x, char *y)
// concateneaza sirul de adresa y la sfarsitul sirului
// de adresa x
{ while (*x) x++;
// avans de adresa pana la sfarsitul sirului x
while (*x++= *y++);
}
funcia compara
int compara (char *x, char *y)
{ while (*x= = *y)
{ if (*x= = \0) return 0;
x++;
y++;
return *x - *y;
// daca diferenta caracterelor este
// negativa atunci x<y altfel x>y
}
}
6.4. ALOCAREA DINAMIC A MEMORIEI
Biblioteca
standard
a
limbajului
C
pune
la
dispoziia
utilizatorului funcii care permit alocarea de zone de memorie n timpul
execuiei programului. O astfel de zon de memorie poate fi utilizat
pentru a pstra date temporare. Zona respectiv poate fi eliberat n
momentul n care nu mai sunt necesare datele care au fost pstrate n ea.
Alocarea de zone de memorie i eliberarea lor n timpul execuiei
programelor permite gestionarea optim a memoriei de ctre programator. Un
astfel de mijloc de gestionare a memoriei l vom numi alocare dinamic a
memoriei.
Vom indica dou funcii din bibloteca limbajului C utilizate
frecvent n alocarea dinamic a memoriei. Prototipurile lor se afl n
fiierele standard alloc.h i stdlib.h, deci pentru a le utiliza vom
include unul din aceste fiiere.
Funcia malloc permite alocarea unui bloc de memorie a crui
dimensiune se specific n octei. Funcia returneaz un pointer spre

nceputul zonei alocate. ntruct acest pointer trebuie s permit


memorarea oricrui tip de dat n zona alocat, el este de tip void *.
Prototipul funciei este:
void *malloc (unsigned n);
unde n este numrul de octei al zonei de memorie care se aloc. n
cazul n care n este prea mare, funcia returneaz pointerul NULL.
Funcia free elibereaz o zon de memorie alocat prin malloc.
Prototipul ei este:
void free (void *p);
unde p este pointerul returnat de malloc
pointerul spre nceputul zonei care se elibereaz.

la

alocare,

deci

este

Exemplu:
Funcia memchar memoreaz un ir de caractere ntr-o zon de memorie
alocat prin funcia malloc. Ea returneaz adresa de nceput a zonei n
care s-a salvat irul de caractere, deci returneaz un pointer spre tipul
char.
#include <stdio.h>
#include <alloc.h>
#include <string.h>
char *memchar (char *s)
{
char *p;
if ((p=(char *)malloc(strlen(s)+1) ) != NULL
{
strcpy (p,s);
return p;
} else
return NULL;
}
Observaii:
1o. n fiierul stdio.h exist definiia constantei NULL.
2o. Fiierul alloc.h s-a inclus deoarece conine prototipul funciei
malloc.
3o. Fiierul string.h conine prototipurile funciilor strlen i
strcpy.
4o. Funcia malloc se apeleaz pentru a rezerva strlen(s)+1 octei;
strlen returneaz numrul de octei cuplai de caracterele proprii ale lui
s (fr caracterul NULL). Cum n zona de memorie rezervat prin malloc se
pstreaz i caracterul NULL, lungimea returnat de funcia strlen s-a
mrit cu 1.
5o. Pointerul returnat de malloc a fost convertit spre char *,
deoarece el este de tip void *. Acest pointer se atribuie lui p, deci p
pointeaz spre nceputul zonei de memorie alocate prin apelul funciei
malloc. Se testeaz dac acest pointer este diferit de NULL (deci dac s-a
putut aloca memoria de dimensiunea cerut). n caz afirmativ, se transfer
irul prin apelul funciei strcpy, returnndu-se apoi valoarea pointerului
p.

6.5. POINTERI SPRE FUNCII


Numele unei funcii este un pointer spre funcia respectiv. El
poate fi folosit ca parametru efectiv la apeluri de funcii. n felul
acesta, o funcie poate transfera funciei apelate un pointer spre o
funcie. Aceasta, la rndul ei, poate apela funcia care i-a fost
transferat n acest fel.
Exemplu:

Un exemplu matematic n care este nevoie de un astfel de transfer


este cel cu privire la calculul aproximativ al integralelor definite. S
presupunem c dorim s calculm integrala definit din funcia f(x), ntre
limitele a i b, folosind formula trapezului:
I= h((f(a)+f(b))/2 +f(a+h)+f(a+2h)+. . . +f(a+(n-1)h)
unde
h=(b-a)/h.
n continuare construim o funcie care calculeaz partea
dreapt a acestei relaii. Numim aria_trapez aceast funcie.
Observaii:
1o. Deoarece funcia f(x) din relaia de mai sus nu este definit n
acest moment, ea trebuie s figureze printre parametrii funciei
aria_trapez, mpreun cu limitele de integrare i valoarea lui n.
2o. Funcia aria_trapez returneaz valoarea aproximativ a integralei
i ea se va apela printr-o expresie de atribuire, de exemplu:
aria=aria_trapez (a, b, n, f);
3o. Funcia aria_trapez returneaz o valoare flotant n dubl
precizie. De asemenea, i funcia f(x) returneaz o valoare flotant n
dubl precizie. De aici rezult c prototipul funciei aria_trapez este
urmtorul:
double aria_trapez (double a, double b, int n, double (*f)());
sau
double aria_trapez (double, double, int , double (*)());
4o. Este necesar ca naintea apelului funciei aria_trapez funcia
f(x) s fie definit sau s fie prezent prototipul ei , de exemplu:
double f();
5o. Construcia double (*f) () se interpreteaz n felul urmtor:
- *f
nseamn c f este un pointer;
- (*f)()
nseamn c f este un pointer spre o funcie;
- double (*f) () nseamn c f este un pointer spre o funcie care
returneaz o valoare flotant n dubl precizie.
6o. Trebuie s se includ *f ntre paranteze, deoarece construcia
double *f(); este corect, dar nseamn altceva, parantezele rotunde fiind
prioritare operatorului unar *. n acest caz, se declar f ca o funcie ce
returneaz un pointer spre o valoare flotant n dubl precizie.
7o. Ultimul parametru formal al funciei aria_trapez corespunde
parametrului efectiv f i deci el trebuie declarat ca i pointer spre o
funcie ce returneaz o valoare flotant n dubl precizie. Conform
observaiei 5), dac p este numele parametrului formal ce corespunde
parametrului efectiv f, atunci p se declar astfel:
double (*p)();
8o. n corpul funciei aria_trapez va trebui s apelm funcia f(x)
pentru a calcula valorile:
f(a), f(b), f(a+h), . . . , f(a+(n-1)h).
n momentul programrii funciei aria_trapez, nu se cunoate numele
funciei concrete, ci numai pointerul p spre ea. De aceea, vom nlocui
numele funciei prin *p, deci vom folosi apelurile:
(*p)(a), (*p)(b), (*p)(a+h), . . . ,(*p)(a+(n-1)h)
double aria_trapez(double x, double y, int m, double(*p)());
{
double h,s;
int i;
h=(y-x)/m;
for (i=1, s=0.0; i<m; i++) s+=(*p)(x+i*h);
s+=((*p)(x) + (*p)(y))/2;
s=h*s;
return s;
}
Vom utiliza funcia aria_trapez pentru a calcula integrala definit
din funcia sin(x2) pe intervalul [0,1], cu o eroare mai mic dect 10 -8.
Vom nota cu In urmtoare sum:

In= h((f(a)+f(b))/2 +f(a+h)+f(a+2h)+. . . +f(a+(n-1)h)


Paii algoritmului sunt urmtorii:
Pasul 1. Se alege o valoare iniial pentru n, de exemplu 10.
Pasul 2. Se calculeaz In.
Pasul 3. Se calculeaz I2n prin dublarea lui n.
Pasul 4. Dac |In-I2n| < 10-8, algoritmul se ntrerupe i valoarea
integralei, cu precizia admis, este I2n; altfel se dubleaz n, se pune
In=I2n; n, i se trece la pasul 3.
#define A 0.0
#define B 1.0
#define N 10
#define EPS 1e-8
#include <stdio.h>
#include <math.h>
double sinxp(double);
// prototipul functiei sin(x*x)
double aria_trapez(double, double, int, double (*)());
void main (void)
// functia principala
{ int n=N;
double in, i2n, vabs;
in=aria_trapez (A, B, n, sinxp);
do {
n=n*2;
i2n=aria_trapez(A, B, n, sinxp);
if ((vabs= in-i2n) < 0)
vabs = -vabs;
in=i2n;
} while (vabs >= EPS);
printf (valoarea integralei este : %g.10\n,i2n);
}
double aria_trapez(double x, double y, int m, double(*p)());
{
double h,s;
int i;
h=(y-x)/m;
for (i=1, s=0.0; i<m; i++) s+=(*p)(x+i*h);
s+=((*p)(x) + (*p)(y))/2;
s=h*s;
return s;
}
double sinxp (double x)
{ return sin (x*x); }
6.6. TRATAREA PARAMETRILOR DIN LINIA DE COMAND
n linia de comand folosit la apelul execuiei unui program se pot
utiliza diferii parametri. Aceti parametri pot fi utilizai folosind
parametrii argc i argv ai funciei principale.
Parametrul argc este de tip ntreg i indic numrul de parametri din
linia de comand.
Parametrul argv este un tablou de pointeri spre zonele n care sunt
pstrai parametrii liniei de comand. Acetia se consider iruri de
caractere.
Astfel antetul funciei principale va fi :
main (int argc, char *argv[ ])
Exemplu:
Considerm c la lansarea programului prog s-au furnizat parametrii:
MARTIE 1956
n acest caz argc=4, iar tabloul argv conine pointerii:
- argv[0] - pointer spre numele programului (calea, numele i
extensia .EXE
- argv[1] - pointer spre irul 31;

- argv[2]
- argv[3]

- pointer spre irul MARTIE;


- pointer spre irul 1991.

Observaii:
1o. Lansarea unui program se face cu prima instruciune a funciei
principale. Deci parametrii argc i argv au deja n acest moment valorile
indicate mai sus, putnd fi analizai chiar cu prima instruciune
executabil.
2o. n mod frecvent, aceti parametrii reprezint diferite opiuni
ale programului, date calendaristice, nume de fiiere, etc.
3o. argv[0] este ntotdeauna pointerul spre numele fiierului cu
imaginea executabil a programului.
void main ( int argc, char *argv[])
{ int i;
for (i=0; i<argc; i++;)
printf (%s\n,argv[i]);
}

// va afisa parametrii din linia de comanda

6.7. MODIFICATORUL const


Am vzut anterior c o constant se definete prin caracterele
care intr n compunerea ei. De asemenea, n acelai capitol s-a artat c
putem atribui un nume unei constante printr-o construcie #define. Un
astfel de nume se spune c este o constant simbolic i el se substituie
prin irul de caractere care i corespunde, n faza de preprocesare.
Un alt mod de a defini o constant este acela de a folosi
modificatorul const ntr-o declaraie. Printr-o astfel de declaraie, unui
nume i se poate atribui o valoare constant. n acest caz, numele respectiv
nu mai este tratat de preprocesor i el poate fi folosit n program n mod
analog cu numele variabilelor. Unui astfel de nume declarat cu ajutorul
modificatorului const nu i se poate schimba valoarea printr-o expresie de
atribuire, ca i unei variabile obinuite.
Formatele declaraiei cu modificatorul const sunt urmtoarele:
tip const nume = valoare;
const tip nume = valoare;
tip const nume;
const tip nume;
const nume = valoare;
const nume;
Exemplu:
void main (void)
{ const i=10;
// i devine egal cu 10; nu este posibila o atribuire i=0
const pi = 3.1415926
char *const s=martie; // s este un pointer constant spre zona in care este
// pastrat sirul de caractere martie. Valoarea lui s
// nu poate fi schimbata dar continutul zonei spre
// care pointeaza s poate fi schimbat
*s= 1;
// schimba litera m cu 1
*(s+1)=2
// schimba litera a cu 2
char const *s=aprilie; // s este un pointer spre o zona constanta.
// valoare lui s poate schimbata dar sirul aprilie
// nu poate fi modificat
const char *s=aprilie
// este identica cu declaratia de mai sus.
}
Modificatorul const se folosete frecvent la declararea
parametrilor formali de tip pointer. O astfel de declaraie are formatul:

const tip *nume_parametru_formal;


Un parametru formal declarat prin construcia :
tip *nume_parametru_formal;
corespunde unui parametru efectiv a crui valoare este o adres. La
apel, valoarea parametrului formal devine egal cu aceast adres. Datorit
acestui fapt, funcia apelat poate s modifice data aflat la adresa
respectiv. Dac se folosete modificatorul const utilizat la declararea
unui astfel de parametru formal atunci se interzice funciei apelate s
modifice data de la adresa recepionat la apel de ctre parametrul formal
corespunztor. Acest mecanism este folosit frecvent n cazul funciilor de
tratare a irurilor de caractere.
De exemplu funcia strlen din biblioteca standard a limbajului
C are prototipul:
unsigned strlen (const char *s);
Ea se apeleaz prin expresii de atribuire de forma:
i=strlen(x);
unde x este un pointer spre o zon de memorie n care se afl un ir
de caractere.
Funcia strlen determin lungimea irului aflat la adresa
recepionat de ctre parametrul s. Ea nu are voie s modifice irul
respectiv i din aceast cauz parametrul s se declar folosind
modificatorul const.
6.8. STIVA
Prin stiv (stack n englez) nelegem o mulime ordonat de
elemente la care accesul se realizeaz conform principiului ultimul venit
primul servit. n englez stiva se mai numete i list LIFO (Last In First
Out).
O modalitate simpl de a implementa o stiv este pstrarea
elementelor ei ntr-un tablou unidimensional. n acest tablou se vor pstra
elementele stivei unul dup altul. De asemenea, ele se pot scoate din
tablou n ordinea invers pstrrii lor. La un moment dat se poate scoate
ultimul element pus pe stiv i numai acesta.
Despre ultinul element pus n stiv se spune c este vrful
stivei, iar despre primul element c este baza stivei.
Accesul este pemis doar la vrful stivei:
un element se poate pune pe stiv numai dup elementul aflat n
vrful stivei i dup aceast operaie el ajunge vrful stivei;
se poate scoate de pe stiv numai elementul aflat n vrful stivei i
dup aceast operaie n vrful stivei rmne elementul care a fost pus pe
stiv naintea lui.
Vom numi stack tablou de tip int afectat stivei i next variabila
care indic prima poziie liber din stiv. Deci stack[0] este baza stivei
iar stack[n] va fi vrful stivei. Vom defini mai multe funcii asociate
tabloului stack:
- push
funcia care pune un element n stiv;
- pop funcia care scoate un element din stiv;
- clear
funcia de iniializare a stivei; dup apelul ei stiva
devine vid;

#define MAX 1000


static int stack[1000];
static next = 0;

// indicele pentru baza stivei

void push(int x)
// pune pe stiva valoarea lui x
{ if (next < MAX)
stack [next++]=x;
else
printf (stiva este depasita\n);
}

int pop()
{ if (next >0)
else

// scoate elementul din varful stivei si returneaza valoarea lui

return stack [--next];


{ printf (stiva este vida\n);
return 0;
}

}
void clear(void)
{
next=0;
}

// videaza stiva

LECIA 7.
RECURSIVITATE
Spunem c o funcie C este recursiv dac ea se autoapeleaz nainte
de a se reveni din ea. Funcia se poate reapela fie direct, fie indirect
(prin intermediul altor funcii).
La fiecare apel al unei funcii, parametrii i variabilele locale se
aloc pe stiv ntr-o zon independent. De asemenea, orice apel recursiv
al unei funcii va conduce la o revenire din funcie la instruciunea
urmtoare apelului respectiv. La revenirea dintr-o funcie se realizeaz
curarea stivei, adic zona de pe stiv afectat la apel parametrilor i
variabilelor automatice se elibereaz.
Un exemplu simplu de funcie apelata recursiv este funcia de calcul
al factorialului. Putem defini recursiv funcia factorial astfel:
factorial(n)= 1,
dac n=0
factorial(n)=n*factorial(n-1),
dac n>0
n limbajul C avem :
double factorial (int)
{ if (n= = 0)
return 1.0;
else
return n*factorial(n-1);
}
Observaii:
1o. n general, o funcie recursiv se poate realiza i nerecursiv,
adic fr s se autoapeleze.
2o. De obicei, recursivitatea nu conduce nici la economie de memorie
i nici la execuia mai rapid a programelor. Ea permite ns o descriere
mai compact i mai clar a funciilor. Acest lucru rezult i din exemplul
de mai sus de calcul al factorialului.
3o. n general, funciile recursive sunt de preferat pentru procese
care se definesc recursiv. Exist i excepii. De exemplu algoritmul de
generare a permutrilor de n obiecte poate fi descris recursiv astfel:
avnd n memorie toate cele (n-1)! permutri, atunci permutrile de n
obiecte se genereaz nsernd pe n n toate poziiile posibile ale fiecrei
permutri de n-1 obiecte. Dar ne aducem aminte c 10!=3628800 i
capacitatea stivei se depete repede.
Exemple:

Programul determin recursiv cmmdc (algoritmul lui Euclid) a dou


numere ntregi (de tip long):
cmmdc (a,b) = b,
dac a%b =0 (restul mpririi lui a la b e
zero)
cmmdc (a,b) = cmmdc (b,a%b), n caz contrar.
#include <iostream.h>
#include <conio.h>
long cmmdc(long a, long b)
{ if (!(a % b))
return b;
else
return cmmdc(b, a % b);
}
void main(void)
{ long x,y;
clrscr();
cout << "dati un numar natural=";
cin >> x;
cout << "dati alt numar natural=";
cin >> y;
cout << "cmmdc(" << x << "," << y << ")=" << cmmdc (x,y);
}
Am folosit funciile de intrare / ieire cin i cout, imediat se
observ modul lor de utilizare.
Programul
unidimensional

determin

recursiv

suma

unor

elemente

de

tablou

a[1]+a[2]+ . . . + a[n]
#include <iostream.h>
#define MAX 100
int a[MAX];
// suma(n)= 0,
// suma(n)=suma(n-1) + a[n]
int suma(int n)
{ if (!n) return 0;
else
return a[n]+suma(n-1);
}

daca n=0
daca n>0

void main(void)
{int n,i;
cout << "dati n= ";
cin >> n;
for (i=1; i<=n; i++)
{
cout << "a[" << i << "]= ";
cin >> a[i];
}
cout << "suma numerelor este " << suma(n);
}
Programul determin recursiv
Fibonacci definit dup cum urmeaz:

termenul

al

fibonacci[0]=0
fibonacci[1]=1
fibonacci[n]=fibonacci[n-1]+fibonacci[n-2],

n-lea

din

dac n>1

irul

lui

#include<iostream.h>
long fibonacci (long n)
{if (!n)
return 0;
else if (n==1) return 1;
else
return fibonacci(n-1) + fibonacci(n-2);
}
void main (void)
{ long n;
cout << "dati n = ";
cin >> n;
cout << "fibo(" << n << ") =" << fibonacci (n);
}
Programul determina maximul dintr-un vector de numere astfel:
M(n)= a[1]
dac n=1
M(n)= max { M(n-1),a[n] }
dac n>1
#include<iostream.h>
#define MAX(x,y) (x > y ? x : y)
int a[100];
int M(int n)
{ if (n= =1)
else
}

return a[1];
return MAX (M(n-1), a[n]);

void main(void)
{int n,i;
cout << "dati n=";
cin >> n;
for (i=1; i<=n; i++)
{
cout << "a[" << i << "]= ";
cin >> a[i];
}
cout << "maximul este " << M(n);
}
5) Programul afiseaz un ir de caractere n mod recursiv, caracter
cu caracter, considernd c irul de caractere este format din primul
caracter(capul) + restul irului de caractere (coada).
#include <iostream.h>
#include <conio.h>
#define max 100
char sir [max];
int n;
void afis (int m)
{ if (m = = n+1) return;
else
{ cout << sir[m];
afis(m+1);
}
}
void main (void)
{int i;
do {
cout << "\ndati lungimea sirului =";
cin >> n;
}
while ( (n< 0) || (n > max));

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


{
cout << "sir[" << i << "]=";
cin >> sir[i];
}
afis(1);
getch();
}
6) Programul ce urmeaz e oarecum asemntor cu exemplul anterior
doar c afieaz irul de caractere de la sfrit spre nceput.
#include <iostream.h>
#include <conio.h>
#define max 100
char sir [max];
void afis (int m)
{ if (m==0)
return;
else
{ cout << sir[m];
afis(m-1);
}
}
void main (void)
{int n,i;
do
{cout << "\ndati lungimea sirului ="), cin >> n;}
while ( (n< 0) || (n > max));
for(i=1; i<=n; i++)
{
cout << "sir[" << i << "]=";
cin >> sir[i];
}
afis(n);
getch();
}
7)
ntregi:

Programul

sorteaz

prin

#define dim 50
#include <stdio.h>
#include <conio.h>
int x[dim+1],i,n;
void tipsir ()
{ for (i=1; i<=n; i++)
{
printf("%3d",x[i]);
if (!(i % 20)) printf ("\n");
}
}
void quik(int st, int dr)
{int i,j,y;
i=st; j=dr; y=x[i];
do {
while ((x[j] >= y) && (i<j)) j - -;
x[i]=x[j];
while ((x[i] <= y) && (i<j)) i++;
x[j]=x[i];
}
while (i != j);
x[i]=y;
if (st < i-1)
quik(st,i-1);
if (i+1 < dr)
quik(i+1,dr);
x[j]=x[i];
}

metoda

quicksort

un

vector

de

numere

void citire (void)


{ int cod = 0;
n = dim+1;
while ( n <= 0 || n > dim || ! cod )
{
printf ("\ndati dim. sir:");
cod=scanf ("%d",&n);
}
i = 1;
while (i<=n)
{ printf ("x[%2d]=",i);
scanf ("%d", &x[i]);
i++;
}
}
void main(void)
{ clrscr();
citire();
clrscr();
printf ("\n\nsir initial\n");
tipsir();
quik(1,n);
printf ("\n\nsir sortat\n");
tipsir();
getche();
}
LECIA 8.
STRUCTURI, TIPURI UTILIZATOR
Dup cum am vzut datele de acelai tip se pot grupa n
tablouri. Limbajul C permite gruparea unor date de tipuri diferite sub alte
forme de organizare numite structuri.
Tablourile au un tip i anume tipul comun elementelor lor.
Astfel, distingem tablouri de tip ntreg, de tip caracter, de tip flotant,
etc. n cazul structurilor, nu mai avem un tip comun. Fiecare structur
reprezint un nou tip de date, tip care se introduce prin declaraia
structurii respective.
Un exemplu simplu de structur este data calendaristic, cu
componentele urmtoare:
ziua;
luna;
anul.
unde: ziua i anul sunt date de tip ntreg iar luna este un tablou de
caractere.
Structura ca i tabloul, este o muline ordonat de elemente.
n exemplul de mai sus se consider c ziua este primul ei element, luna
este al doilea iar anul este ultimul ei element. Trebuie s precizm c
referirea la componentele unei structuri nu se mai face cu ajutorul
indicilor ci prin calificare.
8.1. DECLARAIA DE STRUCTUR
O structur se poate declara n mai multe feluri, astfel:
Formatul 1:
struct nume_structura
{ lista_declaratii
};
Cu ajutorul acestui format se introduce un nou tip de dat cu numele
nume_structur. Lista de declaraii este format din declaraii obinuite.
Tipul data_calendaristica l putem introduce astfel:
struct data_calendaristica

{ int ziua;
char luna[11];
int anul;
};
O astfel de declaraie se numete declaraie de tip. S reinem c
unui nou tip de date nu i se aloc memorie, el este doar contabilizat ca un
nou tip utilizator pe lng tipurile predefinite ale limbajului C.
Formatul 2:
struct nume_structura
{ lista_declaratii
}lista_variabile;
Un astfel de format introduce tipul utilizator nume_structura i
declar o list de varibile n care fiecare element din list are tipul
nume_structur. Prin exemplu urmtor se introduc variabilele dc1 i dc2 ca
date elementare de tipul data_calendaristica i tabloul dc de 13
componente.
struct data_calendaristica
{ int ziua;
char luna[11];
int anul;
} dc1, dc2, dc[13];
Formatul 3:
struct
{ lista_declaraii
} lista_variabile;
Acest format se foloseste dac nu vrem sa dm nume noului tip
structurat i totodat dac nu mai vrem s-l folosim. Deci nu vom mai pute
declara alte date de tipul structurat nou introdus pentru c tipul nu are
nume.
Exemplu:
struct
{ int ziua;
char luna[11];
int anul;
} dc1, dc2, dc[13];
S-au declarat varibilele dc1, dc2 i tabloul dc avnd noul tip
structurat utilizator dar nu se mai dorete s declarm alte date de acest
tip.
Observaii:
1o. Dac se folosete formatul 1 atunci pentru a declara date de
tipul utilizator nou introdus se folosete o construcie de forma:
struct nume_ structura lista_variabile;
Compilatorul aloc memorie varibilelor din lista de variabile,
tratnd aceast construcie ca i declaraiile obinuite.
2o. Componentele unei structuri pot fi ele nsele date structurate. O
component care nu este structurat se numete component elementar.
3o. Ca i n cazul celorlalte tipuri de variabile se pot defini
structuri globale, statice sau automatice. Structurile statice se declar
precednd declaraiile lor prin cuvntul static, iar cele externe prin
cuvntul cheie extern.
4o. Elementele unei date de tip structur pot fi iniializate dup
modelul iniializrii variabilelor care au tipuri predefinite.
Exemple:
1) Introducem tipul utilizator data_calendaristica astfel:
struct data_calendaristica
{ int ziua;

char luna[11];
int anul;
};
pentru a iniializa o dat de tipul data_calendaristic vom scrie:
struct data_calendaristica dc1={31, martie, 1956};
2) Dac declarm un nou tip date_personale i n care vrem s folosim
tipul data_calendaristica, vom scrie:
struct date_personale
{ char nume[30];
char adresa[50];
struct
data_calendaristica
data_angajarii;
};

data_nasterii,

8.2. ACCESUL LA ELEMENTELE UNEI STRUCTURI


Pentru a avea acces la componentele unei date structurate va
trebui s folosim o calificare de forma:
nume_data_structurata.nume_componenta
Astfel dac avem tipul structurat data_calendaristica
exemplele anterioare i declarm data dc astfel:
struct data_calendaristica dc;
atunci pentru a ne referi la componentele
construciile:
dc.ziua
dc.anul
dc.luna (atenie este pointer spre caractere)

datei

dc

introdus

vom

in

folosi

Dac avem declarat un tablou astfel:


struct data_calendaristica tdc[10];
atunci pentru fiecare component i ne vom referi astfel:
tdc[i].ziua
tdc[i].anul
tdc[i].luna (este pointer)
tdc[i].luna[0], tdc[i].luna[1], . . . , tdc[i].luna[11]
Ca i tablourile structurile se pot transfera prin parametrii,
transfernd un pointer spre data structurat respectiv, adic adresa de
nceput a zonei alocate structurii. Deci, printr-un apel de forma:
functie(&data_structurata);
se transfer funciei functie adresa de nceput a zonei alocate
structurii data_structurata. Dac data_structurata este o structura de
tipul tip, atunci antetul funciei functie este urmtorul:
void functie(tip *p)
unde p este pointer spre tipul structurat tip.
Pentru data structurat
funciei functie este:

dc

de

tipul

data_calendaristica

void functie(struct data_calendaristica *p)


iar apelul pentru data dc se face
functie(&dc);

antetul

Printr-un astfel de apel, funcia apelat nu are acces la


numele datei structurate transferate, ci numai la pointerul spre ea. De
aceea se pune problema accesului la componentele datei structurate prin
pointerul la ea. n acest caz numele datei structurate se va nlocui prin
*p. Deci, n cazul datei structurate dc, transferate ca i mai sus, n
locul construciei
dc.zi
vom scrie:
(*p).zi
nlocuind numele datei structurate dc prin *p, unde p este un pointer
spre dc.
Observaie:
1o. Parantezele rotunde din construcia de mai sus sunt obligatorii,
deoarece punctul este un operator prioritar operatorului unar *.
2o. Construcia de mai sus poate fi nlocuit prin p->zi care este
identic cu ea. Simbolul -> se compune din caracterele - i > scrise
unul dup cellalt fr spaiu ntre ele. El se numete sgeat i este
considerat a fi un operator cu aceeai prioritate ca i punctul, deci de
prioritate maxim.
8.3. ATRIBUIRI DE NUME PENTRU TIPURI DE DATE
Dup cum tim tipurile de baz ale limbajului C, numite i
tipuri predefinite se identific printr-un cuvnt cheie (int, char, float,
etc). Totodat prin instruciunea struct, programatorul poate s introduc
un tip nou. Programatorul poate s atribuie un nume unui tip (predefinit
sau utilizator) cu ajutorul construciei:
typedef tip nume_nou_tip;
unde:
tip este numele unui tip predefinit sau al unui tip utilizator
(introdus cu struct);
nume_nou_tip este noul nume atribuit tipului respectiv.
Dup ce s-a atribuit un nou nume unui tip, numele respectiv poate fi
utilizat pentru a declara date de acel tip, la fel cum se utilizeaz n
declaraii cuvintele cheie int, char, float, etc.
Observaii:
1o. De obicei numele atribuit unui tip se scrie cu litere mari.
2o. Un exemplu de astfel de nume exist n fiierul stdio.h pentru
tipul fiier, cruia i s-a atribuit numele FILE.
Exemple:
Fie declaraiile:
typedef int INTREG;
typedef float REAL;
n continuare, denumirile INTREG i REAL se pot folosi la fel ca i
cuvintele cheie int i float. Cu alte cuvinte, declaraia:
INTREG i, j, tablou[10];
este identic cu declaraia urmtoare:
int i, j, tablou[10];
Analog:
REAL x, y, z;
este identic cu declaraia:
float x, y, z;
typedef struct data_calendaristica

int
char
int

ziua;
luna[11];
anul;

} DC;
Prin aceast declaraie se atribuie denumirea DC tipului
structurat data_calendaristica. n continuare putem declara date de tip DC:
DC data_nasterii, data_angajarii;
DC data_curenta ={31,august,1998};
typedef int *PI;
Prin aceast declaraie se introduce un sinonim pentru tipul pointer
spre ntregi: int *.
Putem s declarm n continuare pointeri spre ntregi astfel:
PI p;
care este echivalent cu:
int *p;
4) Declaraia
{
double real;
double imaginar;
} COMPLEX;

typdef struct

introduce numele COMPLEX pentru datele de tip complex.


Funcia urmtoare returneaz modulul unui numr complex:
typedef struct
double real;
double imaginar;
} COMPLEX;
#include <math.h>
double modul (COMPLEX *x)
// returneaza modulul numarului
// spre care pointeaza x
{ return sqrt (x->real * x->real + x->imaginar * x->imaginar);
}
{

8.4. UNIUNE
Limbajul C ofer utilizatorului posibilitatea de a folosi
aceeai zon de memorie pentru a pstra date de tipuri diferite n momente
diferite ale execuiei programului. Astfel, de exemplu, putem utiliza o
zon de memorie pentru a pstra la un moment dat o dat flotant, iar
ulterior s reutilizm aceeai zon pentru o dat ntreag sau de tip
pointer. Reutilizrile zonelor de memorie conduc la utilizarea mai
eficient a acesteia, uneori putndu-se obine o economie substanial a
spaiului de memorie alocat programului.
O
uniune
se
declar
printr-o
construcie
asemntoare
declaraiei de structur. Deosebirea const n nlocuirea cuvntului struct
prin union:
union nume
{ tip_membru_1;
. . .
tip_membru_2;
}
Exemplu:
union u
{ int i;
float f;
double d;
};

Prin aceast declaraie s-a definit tipul de date u. n continuare,


putem declara date de tipul u printr-o declaraie de forma:
union u u1;
unde u1 este o dat de tip u creia i se aloc o zon de memorie care
poate fi utilizat pentru a pstra date de tipurile int, float sau double.
Deoarece tipul double necesit memoria cea mai mare se aloc 8 octei.
Astfel zona u1 poate pstra pe oricare din celelalte componente ale uniunii
dar n momente diferite ale execuiei programului.
Accesul la componentele unei uniuni se face la fel ca i n
cazul structurilor. Astfel, pentru a ne referi la componenta i a uniunii u1
definit n exemplul anterior folosim construcia:
u1.i
sau dac p este pointer spre tipul u declarat prin
union u *p;
atunci construcia
p -> i
permite accesul la componenta i a uniunii spre care pointeaz p.
Pentru a evita erorile legate de evidena n fiecare moment a
datei care se prelucreaz se ataeaz unei uniuni o dat menit s indice
componenta curent. Aceast dat este specificat pentru fiecare uniune. De
exemplu pentru uniunea u1 definit anterior este important s se tie dac
zona de memorie conine un ntreg, un flotant n simpl precizie sau un
flotant n dubl precizie. Se definesc trei constante simbolice:
#define INTREG 1
#define F_SIMPLU 2
#define F_DUBLU 3
Modificm tipul u atand data tip_curent de tip int astfel:
struct u
{int tip_curent;
union {
int
i;
float
f;
double d;
} uu;
};
Declarm structura us astfel:
struct u us;
n acest caz, n momentul n care se pstreaz o dat n zona
rezervat uniunii, se atribuie componentei tip_curent una din constantele
definite anterior:
INTREG, dac se pstreaz un ntreg;
F_SIMPLU dac se pstreaz un flotant n simpl precizie;
F_DUBLU dac se pstreaz un flotant n dubl precizie.
Astfel cnd se folosete componenta de tip int se va asocia
atribuirea:
us.tip_curent=INTREG;
Analog se vor folosi i atribuirile urmtoare cnd se vor folosi
componentele de tip float sau de tip double:
us.tip_curent=F_SIMPLU;
respectiv:
us.tip_curent=F_DUBLU;
n felul acesta, se poate testa, n fiecare moment, tipul de
dat prezent n zona rezervat. Aceasta se poate face printr-o secven de
instruciuni if sau prin intermediul instruciunii switch.

if (us.tip_curent = = INTREG)
else if (us.tip_curent = = FSIMPLU)
else if (us.tip_curent = = FDUBLU)
else eroare

// se foloseste us.uu.i
// se foloseste us.uu.f
// se foloseste us.uu.d

sau folosind switch avem o construcie mai clar de forma:


switch (us.tip_curent)
{ case INTREG:
// se foloseste us.uu.i
break;
case FSIMPLU:
// se foloseste us.uu.f
break;
case FDUBLU
// se foloseste us.uu.d
break;
default:
// eroare
}
Programul urmtor
geometrice:
cerc;
dreptunghi;
ptrat;
triunghi.

calculeaz

ariile

pentru

urmtoarele

figuri

Programul citete datele pentru o figur geometric, calculeaz aria


figurii respective i scrie rezultatul:
La intrare se folosesc urmtoarele formate:
- pentru cerc
C raza;
- pentru dreptunghi
D lungime laime;
- pentru ptrat
P latur;
- pentru triunghi T latur latur latur;
- sfrit fiier
EOF.
n cazul triunghiului, se utilizeaz formula lui HERON pentru
calculul ariei:
aria = sqrt (p*(p-a)(p-b)(p-b))
unde p este semiperimetrul, iar a, b, c sunt cele 3 laturi.
#include <stdio.h>
#include <math.h>
#define PI 3.14159265
#define EROARE -1
#define CERC 1
#define PATRAT 2
#define DREPT 3
#define TRIUNGHI 4
typedef struct
{
int tip;
union
{ double raza
double lp ;
double ld[2] ;
double lt[3] ;
} fig;
}FIG;

// tipul figurii
// cerc
// patrat
// dreptunghi
// triunghi

void main (void)


// calculeaza arii
{
double aria,p;
int i;
char car[2];
FIG zfig;
for(; ;)
// citeste primul caracter,el defineste tipul figurii geometrice
{ printf(se cere o litera mare\n);
if ((i = scanf("%1s",car)) == EOF)
break;
if (i != 1)
{ printf (" se cere o litera mare\n");
continue;
}
zfig.tip = EROARE;
switch(car[0])
{case 'C':
// cerc
printf("se cere raza cercului in flotanta\n");
i = scanf("%lf", &zfig.fig.raza);
if(i != 1)
{ printf("se cere raza cercului in flotanta\n");
break;
}
zfig.tip = CERC;
// se pastreaza tipul figurii
break;
case 'P':
// patrat
printf("se cere latura patratului in flotanta\n");
i = scanf("%lf",&zfig.fig.lp);
if( i !=1)
{ printf("se cere latura patratului in flotanta\n");
break;
}
zfig.tip = PATRAT;
break;
case 'D':
// dreptunghi
printf("se cer laturile dreptunghiului in flotanta\n");
i = scanf("%lf %lf",&zfig.fig.ld[0],&zfig.fig.ld[1]);
if(i != 2)
{ printf("se cer laturile dreptunghiului in flotanta\n");
break;
}
zfig.tip = DREPT;
break;
case 'T':
// triunghi
printf("se cer laturile triunghiului in flotanta\n");
i = scanf("%lf %lf %lf", &zfig.fig.lt[0], &zfig.fig.lt[1],&zfig.fig.lt[2]);
if(i != 3)
{ printf("se cer laturile triunghiului in flotanta\n");
break;
}
zfig.tip =TRI;
break;
printf("laturile nu formeaza un triunghi\n");
break;
default:
printf("se cere una din literele urmatoare\n");
printf("C pentru cerc\n");
printf("P pentru patrat\n");
printf("D pentru dreptunghi\n");
printf("T pentru triunghi\n");
} // sfarsit switch

switch (zfig.tip)
{case CERC:
// aria cercului
printf("raza=%g aria=%g\n", zfig.fig.raza, PI*zfig.fig.raza*zfig.fig.raza);
break;
case PATRAT:
// aria patratului
printf("latura =%g aria=%g\n",zfig.fig.lp, zfig.fig.lp*zfig.fig.lp);
break;
case DREPT:
// aria dreptunghiului
printf("lungimea =%g latimea =%g\n", zfig.fig.ld[0], zfig.fig.ld[1]);
printf("aria=%g\n", zfig.fig.ld[0]*zfig.fig.ld[1]);
break;
case TRIUNGHI:
// aria triunghiului
p=(zfig.fig.lt[0] + zfig.fig.lt[1] + zfig.fig.lt[2])/2;
if(p>zfig.fig.lt[0] && p>zfig.fig.lt[1] && p>zfig.fig.lt[2])
{p=p*(p-zfig.fig.lt[0])*(p-zfig.fig.lt[1])* (p-zfig.fig.lt[2]);
printf("a=%g b=%g c=%g\n", zfig.fig.lt[0], zfig.fig.lt[1], zfig.fig.lt[2]);
printf("aria = %g\n",sqrt(p));
}
else { printf ( laturile nu formeaza un triunghi);
break;
}
default :
// avans pana la newline sau EOF
while ((i = getchar()) != \n && i != EOF);
} // sfarsit switch
if (i = = EOF) break;
}
// sfarsit for
}
// sfarsit main

8.5. CAMP
Limbajul C permite utilizatorului definirea i prelucrarea
datelor pe bii. Utilizarea datelor pe bii este legat de folosirea
indicatorilor care de obicei sunt date care iau numai dou valori 0 sau 1.
Nu este justificat ca un astfel de indicator s fie pstrat ca
un ntreg pe 16 bii i nici mcar pe un octet. Indicatorul poate fi
pstrat pe un singur bit. n acest scop, limbajul C ofer posibilitatea de
a declara date care s se aloce pe bii (unul sau mai muli bii). Acest
lucru i gsete aplicare n programele de sistem. Astfel, de exemplu,
atributele variabilelor dintr-o tabel de simboluri pot fi pstrate pe
bii, ceea ce conduce la o economisire substanial a memoriei ocupate de
tabela respectiv.
Prin camp nelegem un ir de bii adiaceni coninui ntr-un
cuvnt calculator. Cmpurile se grupeaz formnd o structur.
Un cmp se declar ca i o component a unei structuri i el
are tipul unsigned (ntreg fr semn). Totodat n declaraia cmpului se
indic i dimensiunea lui n bii.
n general, o structur cu componente cmpuri are forma:
struct
{ camp1;
. . .
campn;
} nume;
unde campi (i=1,...,n) are unul din formatele de mai jos:
unsigned nume : lungime_n_bii
sau
: lungime_n_bii
Exemplu:
struct
{ unsigned a:1;

unsigned b:1;
unsigned c:2;
unsigned d:2;
unsigned e:3;
} indicatori;
Data indicatori se aloc ntr-un cuvnt calculator, adic pe 16
bii. Componentele ei sunt:
a un bit;
b un bit;
c doi bii;
d doi bii;
e trei bii.
La cmpuri ne putem referi la fel ca i la componentele oricrei
structuri. Deci la indicatorii de mai sus ne putem referi prin urmtoarele
construcii:
indicatori.a
indicatori.b
indicatori.c
indicatori.d
indicatori.e
Alocarea biilor este dependent de calculator. De obicei biii se
aloc de la dreapta spre stnga ca n figura de mai jos:
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
a
b
c
d
e
Observaii:
1o. Dac un cmp nu poate fi alocat n limitele unui cuvnt, el se
aloc n ntregime n cuvntul urmtor.
2o. Nici un cmp nu poate avea o dimensiune mai mare dect 16 bii.
3o. Formatul fr nume (al doilea format) pentru cmp se folosete
pentru cadraje. Acest lucru este util atunci cnd sunt zone de bii
neutilizate n cadrul unui cuvnt. De asemenea, utilizarea formatului cu
lungime egal cu zero permite ca alocarea cmpurilor urmtoare lui s se
fac n cuvntul urmtor.
4o. O structur care are i componente cmpuri poate avea i
componente obinuite.
5o. Nu se pot defini tablouri de cmpuri.
6o. Unui cmp nu i se poate aplica operatorul adres.
Cmpurile se utilizeaz frecvent la scrierea unor programe de sistem,
cum ar fi : drivere pentru periferice, compilatoare, etc.
Utilizarea
cmpurilor
poate
conduce
la
programe
cu
o
portabilitate redus. Totodat, accesul la date pe bii conduce la
creterea numrului de operaii, fiind necesare deplasri i operaii pe
bii suplimentare, fapt ce poate conduce att la creterea timpului de
execuie a programelor, ct i la creterea memoriei utilizate. Ori datele
pe bii se folosesc chiar n ideea de a economisi memorie.
8.6. TIPUL ENUMERAT
Tipul enumerat permite utilizatorului s foloseasc n program
nume sugestive n locul unor valori numerice. De exemplu, n locul
numrului unei luni calendaristice, se poate folosi denumirea ei:
ian

feb
mar
n locul valorilor 0 i 1 se pot folosi cuvintele FALS i ADEVRAT.
Prin aceasta, se introduce o mai mare claritate n programe, deoarece
valorile numerice sunt nlocuite prin diferite sensuri atribuite lor.
Un tip enumerat se introduce printr-o declaraie de forma:
enum nume {nume0, nume1, . . . , numen};
Prin aceast declaraie se definete tipul enumerat nume, iar nume i
are valoarea i. O form mai general a declaraiei de mai sus permite
programatorului s foreze valorile numelor din acolad. n acest scop, se
pot folosi construcii de forma:
numei= eci
unde eci este o expresie constant de tip int.
Cu alte cuvinte, unui nume i se poate atribui o valoare sau
valoarea lui coincide cu a numelui precedent mrit cu 1. Dac primului
nume din acolad nu i se atribuie o valoare, el are valoarea 0. Numele
nume0, nume1,. . . , numen trebuie s fie nume diferite. Ele sunt constante
i valoarea lor se stabilete prin declaraia n care au fost scrise.
Domeniul lor de valabilitate este definit de domeniul de valabilitate al
declaraiei prin care se definesc:
instruciunea compus care conine declaraia;
fiierul surs n care este scris declaraia, dac este extern
oricrei funcii.
Valorile atribuite lui nume0, nume1, . . . , numen sunt de obicei
diferite, dar unele pot s i coincid.
Dup ce s-a introdus un tip enumerat, se pot declara date de
tipul respectiv printr-o declaraie de forma:
enum nume lista_de_variabile;
Datele de tip enumerat se consider de tip int i
utiliza n program oriunde este legal s apar o dat de tip int.
Observaii:
1o. Se pot

utiliza,

ca

cazul

structurilor,

se

pot

construcii

de

forma:
enum nume {nume0, nume1,. . . , numen} lista_de_variabile;
sau
enum { nume0, nume1,. . . , numen} lista_de_variabile;
2o. De asemenea, se poate
atribui un nume unui tip enumerat:

utiliza

construcia

typedef

pentru

typedef enum nume {nume0, nume1,. . . , numen} NUME;


n continuare se pot declara date de tipul NUME, astfel:
NUME lista_de_variabile;
Exemple:
enum luna{ian=1,feb,mar,apr,mai,iun,iul,aug,sep,oct,nov,dec};
enum luna luna_calendaristica
Mulimea de
1,2, . . . ,
ian ia
feb ia
. . .
dec ia

Prin prima declaraie se introduce tipul enumerat luna.


valori asociate acestui tip este format din numerele ntregi
12. Se pot utiliza denumirile:
valoarea 1
valoarea 2
valoarea 12

A doua construcie declar data luna_calendaristica de tipul luna. Ei


i se pot atribui valori prin expresii de atribuire de forma:
luna_calendaristica = mar sau
luna_calendaristica = mai + 4
typedef enum {luni, marti,miercuri,joi,vineri,sambata,duminica} ZI;
ZI z;
Variabila z este de tip ZI. Se poate utiliza n expresii de forma:
z=marti;
if(z<sambata)
// trateaza ziua de lucru
else
// trateaza zi de odihna
LECIA 9.
LISTE
9.1. DATE STRUCTURATE DEFINITE RECURSIV
Limbajul C permite definirea de tipuri structurate recursiv
(autorefereniate). Acest lucru se face cu ajutorul pointerilor, i anume
un element al structurii poate s fie un pointer spre tipul de dat
introdus prin structura respectiv:
struct nume
{ declaratii
struct nume *p;
declaratii
};
Un tip definit ca mai sus se spune c este un tip autoreferit
sau recursiv. O dat structurat declarat printr-un astfel de tip se spune
c este autoreferit sau recursiv. Datele structurate recursive au
numeroase aplicaii n prelucrarea listelor nlnuite i arborescente.
Dou tipuri structurate t1 i t2 pot s conin fiecare un
pointer spre celalalt. n acest caz se va proceda ca mai jos:
struct t1;
// o declaratie inainte fara de care nu
se poate
// declara tipul t2
struct t2
{ declaratii
struct t1 *pt1;
declaratii
};
struct t1
{ declaratii
struct t2 *pt2;
declaratii
};
9.2. LISTE NLNUITE
Datele structurate se pot organiza n tablouri sau n structuri
recursive introducnd n tipul structurat unul sau mai muli pointeri spre
tipul structurat respectiv. Astfel se stabilete o relaie de ordine
(uneori chiar mai multe) ntre elementele mulimii de date structurate; de
asemenea, mulimea rescpectiv se poate organiza n mod dinamic, adugnd
elemente noi sau suprimndu-le pe cele care nu mai sunt necesare.
Definiie O mulime dinamic de structuri recursive de acelai tip i
care satisfac una sau mai multe relaii de ordine introduse prin pointeri
se numete list nlnuit. Elementele listei se mai numesc noduri.
Cele mai utilizate tipuri de list sunt:
lista simplu nlnuit;

lista circular simplu nlnuit;


lista dublu nlnuit;
lista circular dublu nlnuit;.
Cele patru tipuri de liste sunt exemplificate grafic astfel:
capul

list liniar simplu nlnuit;


capul

list liniar circular simplu nlnuit;


capul

list liniar dublu nlnuit;

capul

list liniar circular dublu nlnuit;


9.3. LISTA LINIAR SIMPLU NLNUIT
O list simplu nlnuit; este o list nlnuit; ale crei noduri
satisfac o singur relaie de ordine introdus prin pointeri.
Tipul unui nod dintr-o list simplu nlnuit; se poate declara n
dou moduri:
struct tnod
{ declaratii
struct tnod *urmator;
declaratii
};
typedef struct tnod
{ declaratii
struct tnod *urmtor;
declaratii
} TNOD;
Observaii
1o. Varianta b) este mai folosit.

2o. Pointerul urmtor introduce o relaie de ordine ntre nodurile de


tip TNOD.
3o. Ultimul nod al listei va avea pointerul urmator = NULL.
4o. Pentru nodurile interioare ale listei pointerul urmator va avea
valori adrese; dac urmator din nodul a pointeaz spre nodul b, spunem c
nodul b este succesorul lui a.
Operaiile ce se pot efectua asupra unei liste simplu nlnuit;
crearea listei;
accesul la un nod al listei;
inserarea unui nod nlnuit;
tergerea unui nod dintr-o list;
tergerea unei liste.
Memorarea listelor se poate face:
dinamic (n memoria intern);
static (n fiiere).
Pentru modul dinamic se va folosi funcia malloc la crearea listei
ct i la inserarea de noduri, iar la tergerea de noduri funcia free.
9.4. CREAREA I AFIAREA UNEI LISTE
Vom crea o list ce conine n noduri informaii despre numere
ntregi i ptratele lor. Avem dou funcii: una de creare care ntoarce
adresa capului listei i o funcie de afiare a informaiei din noduri. Vom
comenta instruciunile importante din program.

#include <stdio.h>
#include <alloc.h>
typedef struct nod
// definirea tipului NOD
{
int nr;
int patrat;
struct nod* leg;
} NOD;

NOD *creaza(void)
// functia de creare, intoarce adresa capului
{ NOD *cap,*p,*pc;
int i,lung;
printf("\n\n\n\ creare lista simplu inlantuita\n\n");
lung = sizeof(NOD);
pc=(NOD *)malloc(lung);
// pc este un pointer curent, in el se vor pune adresel noi
cap=pc;
printf("dati informatia elementului : ");
scanf ("%d",&i);
while (i>0)
// crearea listei se termina la numar negativ
{ p=pc;
// pointer ce pastreaza adresa noua
p->nr=i;
// incarcarea informatiei
p->patrat=i*i;
pc=(NOD *)malloc(lung);
// se cere o noua adresa de memorie
p->leg=pc;
// se leaga pointerul leg la noua adresa
printf("dati informatia elementului : ");
scanf ("%d",&i);
}
p->leg=NULL;
// ultimul nod al listei are pointerul leg = NULL
free(pc);
// eliberarea ultimei adrese care de fapt nu face parte din lista
return cap;
// returneaza adresa capului listei
}

void afisare(NOD *p)


// functia de afisare a listei
{
while (p != NULL)
// cat timp n-am ajuns la ultimul nod
{ printf ("\n numarul %d si patratul sau %d", p->nr,p->patrat);
p=p->leg;
// trecerea la urmatorul nod al listei
}
}
void main (void)
// functia principala
{ NOD *capul;
clrscr();
capul=creaza();
// lista e cunoscuta prin adresa capului
afisare(capul);
getch();
} LECIA 10.
PRELUCRAREA FIIERELOR
10.1. FIIERE
n general, prin fiier nelegem o colecie ordonat de
elemente numite nregistrri, care sunt pstrate pe diferite suporturi de
memorie extern. Suportul de memorie extern cel mai folosit este suportul
magnetic (de obicei discuri sub forma de flopy i hardiscuri sau band
magnetic care e din ce n ce mai rar folosit). Suportul magnetic este
reutilizabil deoarece zona utilizat pentru a pstra nregistrrile unui
fiier poate fi ulterior reutilizat pentru a pstra nregistrrile altui
fiier.
Datele introduse de la un terminal se consider c formeaz un
fiier de intrare. nregistrarea n acest caz, de obicei, este format din
datele tastate la terminal pe un rnd deci caracterul de rnd nou (newline)
este terminator de nregistrare. n mod analog, datele care se afieaz pe
terminal formeaz un fiier de ieire. nregistrarea poate fi format din
caracterele unui rnd.
Un fiier are o nregistrare care marcheaz sfritul de
fiier. n cazul fiierelor de intrare de la tastatur sfritul de fiier
se genereaz prin:
CTRL/Z
El poate fi pus n eviden folosind constanta simbolic EOF definit
n fiierul stdio.h.
Prelucrarea fiierelor implic un numr de operaii specifice
acestora. Dou operaii sunt absolut necesare la prelucrarea oricrui
fiier:
deschiderea fiierului;
nchiderea fiierului.
Aceste operaii de deschidere i nchidere a unui fiier se pot
realiza prin intermediul unor funcii speciale din biblioteca standard a
limbajului C. Alte operaii privind prelucrarea fiierelor sunt:
crearea unui fiier;
consultarea unui fiier;
actualizarea unui fiier;
adugarea de nregistrri ntr-un fiier;
poziionarea ntr-un fiier;
tergerea unui fiier.
Prelucrarea fiierelor se poate face la dou nivele. Primul nivel
face apel direct la sistemul de operare i se numete nivelul inferior de
prelucrare al fiierelor. Cel de-al doilea nivel de prelucrare se
realizeaz prin utilizarea unor proceduri specializate n prelucrarea
fiierelor care, printre altele, pot rezerva i gestiona automat zone
tampon necesare realizrii operaiilor de intrare/ieire, i se numete
nivelul superior de prelucrare al fiierelor

10.2. NIVELUL INFERIOR DE PRELUCRARE AL FIIERELOR


La acest nivel de prelucrare se folosesc 5 funcii:
open (creat)- pentru deschiderea fiierelor;
read - pentru citirea din fiier;
write - pentru citirea din fiier;
lseek - pentru poziionarea n fiier;
close - pentru nchiderea fiierului.
10.2.1. Deschiderea unui fiier
Orice fiier nainte de a fi prelucrat trebuie deschis. Aceast
operaie se realizeaz prin intermediul funciei open al crui prototip
este urmtorul:
int open (const char *cale, int acces);
unde:
cale este un pointer spre un ir de caractere care definete calea
spre fiierul care se deschide (n cea mai simpl form este numele
fiierului dac se afl n directorul curent)
acces este o variabil de tip ntreg care poate lua una din valorile:
- O_RDONLY - fiierul se deschide numai n citire (consultare);
- O_WRONLY - fiierul se deschide numai n scriere (creare);
(sau O_CREAT)
- O_RDWR
- fiierul se deschide n citire/scriere;
- O_APPEND - fiierul se deschide la sfrit pentru adugare;
- O_BINARY - fiierul se prelucreaz binar;
- O_TEXT
- fiierul este de tip text.
Unele valori din cele de mai sus se pot combina cu ajutorul
operatorului |. De exemplu O_RDWR | O_BINARY pentru deschiderea fiierului
n scriere/citire binar.
Observaii:
1o. Funcia open ntoarce descriptorul de fiier care este o valoare
intreag ce va identifica fiierul n toate celelate operaii care se vor
realiza asupra lui. Dac deschiderea unui fiier nu reuete (de obicei
unul din parametrii este eronat) atunci funcia open returneaz valoarea
1.
2o. Pentru a putea utiliza funcia open trebuie incluse fiierele
header io.h i fcntl.h.
3o. Pentru a crea un fiier nou se va folosi funcia creat n locul
funciei open cu prototipul:
int creat (const char *cale, int mod);
unde:
- cale are aceeai semnificaie ca i la funcia open;
- mod este un ntreg care poate fi definit prin constantele simbolice
de mai jos:
S_IREAD
- se poate citi fiierul;
S_IWRITE
- se poate scrie n fiier;
S_IEXEC
- se poate executa programul coninut n fiier.
Utilizarea funciei presupune includerea fiierelor io.h i stat.h
4o. Implicit fiierul se consider c este de tip text.
Exemple:
char nume_fisier[ ]=fis1.dat;
int df;
df = open (nume_fisier, O_RDONLY);
Prin apelul de mai sus se deschide n citire fiierul fis1.dat din
directorul curent.

int df;
df = open (c:\\borlandc\\help.txt,O_APPEND);
Se deschide n adugare fiierul help.txt din directorul borlandc de
pe discul C.
10.2.2. Citirea dintr-un fiier (consultare)
Funcia folosit pentru operaia de citire dintr-un fiier n
memorie se numete read i are prototipul urmtor:
int read (int df, void *buf, unsigned lung);
unde:
- df este descriptorul de fiier a crui valoare a fost definit la
deschidere;
buf este pointerul spre zona de memorie n care se recepioneaz
nregistrarea care se citete;
lung este lungimea n octei a inregistrrii citite.
Observaii:
1o. La fiecare apel funcia returneaz nregistrarea curent. La
primul apel se citete prima nregistrare din fiier, la al doilea apel se
citete a doua, etc. Ordinea nregistrrilor n fiier este cea definit la
crearea fiierului.
2o. La un apel al funciei read se citesc cel mult lung octei
nregistrarea avnd definit lungimea la scrierea n fiier. Funcia
rentoarce numrul de octei citii, 0(zero) la sfrit de fiier, sau 1
la eroare. De obicei se folosesc frecvent nregistrri de 512 octei sau
chiar mai mari.
3o. Funcia read poate fi folosit pentru a citi de la intrarea
standard. n acest caz, descriptorul de fiier este 0 (stdin are 0, stdout
are 1, stderr are 2 stdprn are 3 stdaux are 4). Programatorul nu trebuie s
deschid fiierele standard deoarece ele sunt deschise automat la lansarea
n execuie a programului.
4o. Utilizarea funciei read, presupune includerea fiierului io.h.

10.2.3. Scrierea ntr-un fiier (creare, actualizare, adugare)


Pentru a scrie ntr-un fiier se folosete funcia write. Se
presupune c fiierul este deschis n prealabil prin funcia creat sau
open. Ea este asemntoare cu funcia read, doar c realizeaz transferul
invers, adic din memorie n fiier i are prototipul:
int write (int df, void *buf, unsigned lung);
Observaii:
1o. Funcia returneaz numrul octeilor scrii n fiier. Acesta
este egal cu lung i definete lungimea nregistrrii scrise n fiier. n
cazul n care numrul returnat de funcia write difer de parametrul lung
scrierea a fost eronat i se rentoarce valoarea 1.
2o. Utilizarea funciei write implic includerea fiierlui io.h.
10.2.4. Poziionarea ntr-un fiier
folosete

Pentru a avea acces aleator la nregistrrile unui fiier se


funcie de poziionare n fiier pe anumite nregistrri

dorite. Pe fiierele care au suporturi magnetice este posibil poziionarea


cu ajutorul funciei lseek care are prototipul urmtor:
long lseek (int df, long deplasament, int origine)
unde:
df
este descriptorul de fiier;
deplasament definete numrul de octei peste care se va deplasa
capul de
scriere/citire al discului;
origine
are una din valorile:
0 deplasamentul se consider de la nceputul fiierului;
1 deplasamentul se consider din poziia curent a capului de
scriere/citire;
2 deplasamentul se consider de la sfritul fiierului.
Observaii:
1o. Funcia returneaz poziia capului de citire/scriere fa de
nceputul fiierului n numr de octei sau 1L la eroare.
2o. Funcia nu realizeaz nici un transfer de informaie ci doar
poziioneaz capul de citire/scriere n fiier. Deci pentru transfer e
nevoie de funciile read sau write.
3o. Utilizarea funciei presupune includerea fiierului io.h.
4o. Apelul lseek (df, 0L, 0) permite o poziionare la nceput de
fiier
iar apelul lseek (df, 0L, 2) permite o poziionare la sfrit de
fiier.

10.2.5. nchiderea unui fiier


La sfritul prelucrrii unui fiier acesta trebuie nchis.
Acest lucru se realizeaz automat dac programul se termin prin apelul
funciei exit. Programatorul poate nchide un fiier folosind funcia
close. Se recomand nchiderea unui fiier de ndat ce s-a terminat
prelucrarea lui. Aceasta din cauz c numrul fiierelor ce pot fi deschise
simultan este limitat. Limita este dependent de sistemul de operare i ea
variaz, de obicei, n intervalul 15-25. De obicei numrul de buffere (zone
tampon) asociate fiierelor se precizeaz n fiierul autoexec.bat.
Menionm c fiierele standard din limbajul C nu se nchid de programator.
Funcia close are prototipul urmtor:
int close (int df);
unde
df este descriptorul fiierului care se nchide.
Observaii:
1o. La o nchidere normal, funcia returneaz valoarea 0 i 1 n
caz de incident.
2o. Utilizarea funciei close implic includerea fiierului io.h.
Exemple
Vom deschide fiierul fis1.dat n creare i vom scrie n el dou
nregistrri.
#include<io.h>
#include<fcntl.h>
void main (void)
{ int df,i;
df = open("fis1.dat", O_CREAT);
if (df != -1)
{ write (df,"cioban vasyle\n", 14);
write (df,"cioban andrei\n", 14);
} else printf (nu s-a deschis fisierul);
close (df);

// se deschide fisierul fis1.dat in creare


// se testeaza daca fiserul s-a deschis corect
// se scriu doua inregistrari

// se inchide fisierul

}
Se afieaz coninutul fiierului fis1.dat
#include<io.h>
#include<fcntl.h>
#include <stdio.h>
#include <conio.h>
void main (void)
{int df;
char s[14];
df = open("fis1.dat", O_RDONLY);
// se deschide fisierul n citire
if (df != -1)
// se testeaza daca deschiderea e corecta
{ read (df, s, 14);
printf ("%.14s",s);
read (df, s, 14);
printf ("%.14s",s);
} else printf (nu s-a deschis fisierul);
close (df);
getch();
// se asteapta un caracter de la tastatura
}
programul urmtor copiaz intrarea standard la ieierea standard
folosind o zon tampon de 80 de caractere:
#define LZT 80
#include <io.h>
void main (void)
// copiaza intrarea standard la iesirea standard
{ char zona_tampon[LZT];
int i;
while ((i = read (0, zona_tampon, LZT)) > 0) write (1, zt, i);
}
10.3. NIVELUL SUPERIOR DE PRELUCRARE A FIIERELOR
Dup cum am amintit, la acest
ajutorul unor proceduri specializate.

nivel

fiierele

se

prelucreaz

cu

Deschiderea unui fiier


Funcia fopen se utilizeaz pentru deschiderea unui fiier. Ea
returneaz un pointer spre tipul FILE (tipul fiier), tip definit n
fiierul stdio.h. Tipul FILE este un tip structurat i el depinde de
sistemul de operare. n caz de eroare, funcia fopen returneaz pointerul
NULL. Prototipul funciei fopen este urmtorul:
FILE *fopen (const char *cale, const char *mod);
unde:
cale are aceeai semnificaie ca i n cazul

funciilor

open

creat.
mod este un pointer spre un ir de caractere care definete modul de
prelucrare al fiierului dup deschidere. Acest ir de caractere se
definete n felul urmtor:
- r
- deschidere n citire (read);
- w
- deschidere n scriere (write);
- a
- deschidere pentru adugare;
- r+
deschidere
pentru
modificare
(citire
sau
scriere);
- rb
- citire binar;
- wb
- scriere binar;
- r+b
- citire/scriere binar.
Observaii:

1o. Dac se deschide un fiier inexistent cu modul w sau a,


atunci el este deschis n creare.
2o. Dac se deschide un fiier existent cu modul w, atunci
coninutul vechi al fiierului se pierde i se va crea unul nou cu acelai
nume.
3o. Menionm c, stdin, stdout, stderr, stdaux i stdprn sunt
pointeri spre tipul FILE i permit ca funciile de nivel superior de
prelucrare a fiierelor s poat trata intrarea standard i ieirile
standard pe terminal i imprimant la fel ca i fiierele pe celelalte
suporturi. Singura deosebire const n aceea c aceste fiiere nu se
deschid i nici nu se nchid de ctre programator. Ele sunt deschise
automat la lansarea n execuie a programului i se nchid la apelul
funciei exit.
4o. Apelul funciei se realizeaz prin construcia:
FILE *pf;
pf = fopen (FIS1.DAT,w);
10.3.2. Prelucrarea pe caractere a unui fiier
Fiierele pot fi
folosind dou funcii simple:
putc pentru scriere;
getc pentru citire.

scrise

citite

caracter

cu

caracter,

Funcia putc are prototipul:


int putc (int c, FILE *pf);
unde:
c este codul ASCII al caracterului care se scrie n fiier;
pf este pointerul spre tipul FILE a crui valoare a fost returnat de
funcia fopen la deschiderea fiierului n care se scrie; pf poate fi i
stdout, sdterr, stdaux, stdprn.
Funcia putc returneaz valoarea lui c respectiv 1 n caz de eroare.
Funcia getc are prototipul:
int getc (FILE *pf);
unde:
pf este pointerul spre tipul FILE a crui valoare a fost returnat de
funcia fopen la deschiderea fiierului; n particular pf poate fi stdin.
Funcia getc returneaz codul ASCII al caracterului citit sau EOF la
sfrit de fiier sau eroare.
nchiderea unui fiier
nchiderea unui fiier se realizeaz cu ajutorul funciei fclose care
are prototipul:
int fclose (FILE *pf);
unde:
pf este pointerul spre tipul FILE a crui valoare a fost definit la
deschiderea fiierului prin intermediul funciei fopen.
Funcia fclose returneaz:
0 la nchiderea normal a fiierului;
1 n caz de eroare.
Exemple:
Programul urmtor copiaz intrarea
stdout, folosind funciile getc i putc.

standard

la

ieirea

#include <stdio.h>
void main (void)
{ int c;
while (( c = getc (stdin)) != EOF) putc (c, stdout);
}
Programul urmtor copiaz intrarea standard la imprimant.

standard

#include <stdio.h>
void main (void)
{ int c;
while (( c = getc (stdin)) != EOF) putc (c, stdprn);
}
Programul urmtor scrie la ieirea stdout caracterele unui fiier a
crui cale este argumentul din linia de comand. Dac nu exist un argument
n linia de comand, atunci se citete de la intrarea standard.
#include <stdio.h>
void main (int argc, char *argv[ ] )
{ FILE *pf;
int c;
if (argc = = 1)
pf = stdin;
// nu exista argument in linia de comanda
else
// se deschide fisierul a carui cale se afla in argv[1]
if (( pf = fopen (*++argv,r)) = = NULL)
{ printf (nu se poate deschide fisierul %s\n,*argv);
exit (1);
}
while (( c = getc (pf)) != EOF) putchar(c);
exit (0);
}
Operaiile de intrare-ieire cu format
Biblioteca standard a limbajului C conine funcii care permit
realizarea operaiilor de intrare/ieire cu format. Astfel se pot utiliza
funciile fscanf i fprintf, prima pentru citire cu format dintr-un fiier,
iar a doua pentru scriere cu format ntr-un fiier.
Funcia fscanf este asemntoare cu funcia scanf. Ea are un
parametru n plus fa de scanf. Acest parametru este un pointer spre tipul
FILE i el definete fiierul din care se face citirea. Acest pointer este
primul parametru al funciei fscanf. Funcia poate fi apelat printr-o
expresie de atribuire de forma:
nr = fscanf (pf, control, lista_de_parametrii );
unde :
pf este un pointer spre tipul FILE i valoarea lui a fost definit
prin apelul funciei fopen; definete fiierul din care se face citirea;
ceilali parametri sunt identici cu cei utilizai la apelul funciei
scanf.
Funcia fscanf, ca i funcia scanf, returneaz numrul cmpurilor
citite din fiier. La ntlnirea sfritului de fiier se returneaz
valoarea EOF definit n fiierul stdio.h. Pentru pf = stdin, funcia
fscanf este identic cu scanf.
Funcia fprintf , ca i funcia printf, returneaz numrul
caracterelor scrise n fiier sau 1 n caz de eroare. Pentru pf = stdout,
funcia fprintf este identic cu printf. De asemenea, se pot utiliza
pointerii standard obisnuii: stderr, stdaux, stdprn.
Exemplu:
Vom scrie un program cu ajutorul cruia se va crea un fiier cu numele
"FIS.DAT" i care conine nregistrri cu numele, prenumele i adresa unor
persoane.
#include <stdio.h>
void main(void)
{
int n=0, i=1;
// n este numarul de nregistrari ce se va scrie in fisier
char nume[25], prenume[30], adresa[50];

FILE *pf;
printf("\n Dati numarul de inregistrari n= ");
scanf("%d",&n);
pf=fopen("FIS.DAT","w");
if (pf = = NULL)
{ printf ("Eroare la deschidere");
return;
}
do
{
printf("\n Nume : ");
scanf("%s",nume);
printf("\n Prenume : ");
scanf("%s",prenume);
printf("\n Adresa : ");
scanf("%s",adresa);
fprintf(pf,"%s %s %s", nume, prenume, adresa);
i++;
} while (i<=n);
fclose(pf);
}
10.3. 5. Intrri-ieiri de iruri de caractere
Biblioteca standard a limbajului C conine funciile fgets i
fputs care permit citirea respectiv scrierea ntr-un fiier ale crui
nregistrri sunt iruri de caractere.
Funcia fgets are prototipul:
char *fgets (char *s, int n, FILE *pf);
unde:
s este pointerul spre zona n care se face citirea caracterelor;
n-1 este numrul maxim de caractere care se citesc;
pf este pointerul spre tipul FILE care definete fiierul din care se
face citirea.
De obicei s este numele unui tablou de tip char de dimensiune cel
puin n. irul se termin cu \0 (caracterul NUL). La ntlnirea
caracterului \n, citirea se oprete. n acest caz, n zona receptoare se
transfer caracterul \n i apoi caracterul NUL (\0).
n mod normal, funcia returneaz valoarea pointerului s. La
ntlnirea sfritului de fiier se returneaz valoarea NULL.
Funcia fputs scrie ntr-un fiier un ir de caractere care se
termin prin \0. Ea are prototipul:
int fputs (const char *s, FILE *pf);
unde:
s este pointerul spre zona care conine irul de caractere care se
scrie;
pf este pointerul spre zona care conine irul de caractere care se
scrie.
Funcia fputs returneaz codul ASCII al ultimului caracter scris sau
1 n caz de eroare.
Aceste funcii sunt realizate folosind funcia getc pentru fgets i
putc pentru fputs.
Pentru a citi de la intrarea standard stdin, se poate folosi funcia
gets, care nu mai are parametrii pf i n. Parametrul pf este implicit
stdin. Funcia gets citete caracterele de la intrarea standard pn la
ntlnirea caracterului \n care nu mai este pstrat n zona spre care
pointeaz s. irul de caractere citit se termin i n acest caz prin \0.

n mod analog, pentru a scrie la ieirea standard stdout se poate


folosi funcia puts, care nu mai are parametrul pf, acesta fiind implicit
stdout. n rest, funcia puts este la fel ca i funcia fputs.
10.3.6. Poziionarea ntr-un fiier
Cu ajutorul funciei fseek se poate deplasa capul de
citire/scriere
al
discului
n
vederea
prelucrrii
nregistrrilor
fiierului ntr-o ordine oarecare, diferit de cea secvenial (acces
aleator). Aceast funcie este asemntoare cu funcia lseek. Ea are
prototipul urmtor:
int fseek (FILE *pf, long deplasament, int origine);
unde:
pf este pointerul spre tipul FILE care definete fiierul n care se
face poziionarea capului de citire/scriere;
deplasament i origine au aceeai semnificaie ca i n cazul
funciei lseek.
Funcia fseek returneaz valoarea zero la poziionare corect i o
valoare diferit de zero n caz de eroare.
Funcia ftell indic poziia capului de citire n fiier. Ea are
prototipul:
long ftell (FILE *pf);
unde:
pf este pointerul spre tipul FILE care definete fiierul n cauz.
Funcia returneaz o valoare de tip long care definete poziia
curent a capului de citire/scriere, i anume reprezint deplasamentul n
octei a poziiei capului fa de nceputul fiierului.
10.3.7. Prelucrarea fiierelor binare
Fiierele
organizate
ca
date
binare
(octeii
nu
sunt
considerai ca fiind coduri de caractere) pot fi prelucrate la acest nivel
folosind funciile fread i fwrite. n acest caz se consider c
nregistrarea este o colecie de date structurate numite articole. La o
citire, se transfer ntr-o zon special, numit zon tampon, un numr de
articole care se presupune c au o lungime fix. n mod analog, la scriere
se transfer din zona tampon un numr de articole de lungime fix. Cele
dou funcii au prototipurile de mai jos:
unsigned fread (void *ptr, unsigned dim, unsigned nrart, FILE *pf);
unde:
- ptr este pointerul spre zona tampon ce conine articolele citite
(nregistrarea citit);
- dim
este un ntreg ce reprezint lungimea unui articol;
- nrart
este un ntreg ce reprezint numrul articolelor care se
transfer;
- pf este un pointer spre tipul FILE care definete fiierul din
care se face citirea.
unsigned fwrite (const void *ptr, unsigned dim, unsigned nrart, FILE
*pf);
Parametrii funciei fwrite au aceeai semnificaie ca i parametrii
funciei fread.
Ambele funcii returneaz numrul articolelor transferate sau 1 n
caz de eroare.
Exemplu:

Programul urmtor citete de la intrarea standard stdin datele ale


cror formate sunt definite mai jos i le scrie n fiierul miscari.dat din
directorul curent.
tip
1

denumire
TELVIZOR

um
buc

cod
0001

pret
3000000

cantitate
200

#include <stdio.h>
#define MAX 60
typedef struct { char tip[2];
char den[MAX];
int val;
char unit[3];
long cod;
float pret;
float cant;
} ARTICOL;

// 6 articole in zona tampon


union {
ARTICOL a[6];
char zt[6*sizeof(ARTICOL)];
}buf;
int cit (ARTICOL *str)
// citeste datele de la intrarea standard si le scrie in
{
// structura spre care pointeaza str
int nr,c;
float x,y;
while ((nr = scanf("%1s %60s %3d %2s %ld", str -> tip, str ->den,
&str ->val, str -> unit,&str -> cod)) != 5|| scanf("%f %f", &x,&y) != 2)
{
if(nr = = EOF) return (EOF);
printf ("rind eronat ; se reia\n");
// avans pina la newline
while ((c = getchar ()) != '\n' && c != EOF);
if (c == EOF) return (EOF);
}
// sfarsit while
str -> pret = x;
str -> cant = y;
return (nr);
}
// sfarsit cit
void main (void)
// creaza fisierul miscari.dat cu datele citite de la intrarea standard
{
FILE *pf;
ARTICOL a;
int i,n;
if((pf = fopen("miscari.dat", "wb")) == NULL)
{
printf("nu se poate deschide in creare fisierul miscari.dat\n");
exit(1);
}
for ( ; ; )
{
// se umple zona tampon a fisierului
for (i = 0 ;i<6 ; i++)
{
if((n=cit(&a)) == EOF) break;
buf.a[i]=a;
}
// se scrie zona tampon daca nu este vida
if(i !=0 )

if(i!=fwrite(buf.zt, sizeof(ARTICOL), i, pf))


{ printf("eroare la scrierea in fisier\n");
exit(1);
}
if(n = = EOF) break;
}
fclose(pf);
}
10.4. TERGEREA UNUI FIIER
Un fiier poate fi ters apelnd funcia unlink. Aceasta are
prototipul:
int unlink (const char *cale);
unde:
- cale
este un pointer spre un ir de caractere identic cu cel
utilizat la crearea fiierului n funcia creat sau fopen.
Funcia unlink returneaz valoarea zero la o tergere reuit,
respectiv -1 n caz de eroare.
LECIA 11.
FUNCII STANDARD
Vom descrie cteva clase de funcii, numite clase de funcii
standard aflate n bibliotecile mediului BORLAND C. Funciile i macrourile
utilizate frecvent n majoritatea aplicaiilor se pot grupa n urmtoarele
clase:
funcii de prelucrare a fiierelor;
funcii de alocare dinamic a memoriei;
macrouri de clasificare;
macrouri de transformare a simbolurilor;
funcii care realizeaz conversii;
funcii de prelucrare a irurilor de caractere;
funcii de calcul;
funcii pentru controlul proceselor;
funcii de gestiune a datei i a orei;
funcii de gestiune a ecranului.
Funciile
anterioare.

de

la

punctele

a)

b)

au

fost

descrise

lecii

11.1. MACROURI DE CLASIFICARE


n aceast clas distingem un numr de macrouri simple care au
o utilizare larg n prelucrarea simbolurilor. Definiiile acestor macrouri
se afl n fiierul ctype.h
Unul dintre macrouri este denumit isascii i are prototipul:
int isascii (int c);
Macroul returneaz o valoare diferit de zero dac valoarea lui
c aparine intervalului de numere ntregi [0,127] i zero n caz contrar.
Acest macrou permite s se testeze dac valoarea parametrului su
reprezint un cod ASCII sau nu.
Celelalte macrouri au prototipul urmtor:
int nume (int c);
unde nume este unul din urmtoarele:
isalpha
dac c este codul unei litere;
isalnum
dac c este codul unei litere sau cifre;
isdigit
dac c este codul unei cifre;
isgraph
dac c este codul unui caracter imprimabil inclusiv
spaiul;
islower
dac c este codul unei litere mici;

isprint
dac c este codul unui caracter imprimabil
inclusiv spaiu;
isspace
dac c reprezint spaiu, tabulator, retur de car, rnd
nou,
tabulator vertical, salt la nceput de pagin de imprimant.
isupper
dac c este codul unei litere mari;
isxdigit
dac c este codul unei cifre hexazecimale.

Exemplu:
Programul urmtor citete un fiier
literele mari cu litere mici. Cile spre cele
destinaie) sunt argumente n linia de comand:
argv[1]
este un pointer spre
argv[2]
este un pointer spre

i l rescrie schimbnd
dou fiiere (surs i
fiierul surs;
fiierul destinaie.

#include <stdio.h>
#include <ctype.h>
void main ( int argc, char *argv[ ] )
{ FILE *pf1, *pf2;
int c;
if (argc != 3)
{ printf (linia de comanda eronata\n);
exit(1);
}
if (( pf1 = fopen (argv[1],r) ) = = NULL
{ printf (nu se poate deschide fisierul%s\n, argv[1]);
exit(1);
}
if (( pf2 = fopen (argv[2],w) ) = = NULL
{ printf (nu se poate deschide fisierul%s\n, argv[2]);
exit(1);
}
while (( c = getc (pf1)) != EOF)
if (isascii(c) && isupper (c))
putc (c - A + a, pf2);
// c este codul unei litere mari
else
putc(c, pf2);
// c este codul unei litere mici
fclose (pf1);
fclose (pf2);
// afisarea fisierului destinatie
if (( pf2 = fopen (argv[2], r) = = NULL)
{ printf (nu se poate deschide in citire:%s\n, argv[2]);
exit(1);
}
while ((c = getc(pf2)) != EOF) putchar (c);
fclose (pf2);
}
11.2. MACROURI DE TRANSFORMARE A SIMBOLURILOR
n aceast clas distingem macrouri definite tot n fiierul
ctype.h. Prototipurile acestor macrouri sunt:
int toascii (int c);
returneaz ultimii 7 bii ai lui c
(care reprezint un
cod ASCII);
int tolower (int c);
transform pe c din liter mare n
liter mic;

int toupper (int c);

transform

pe

din

liter

mic

liter mare.
11.3. CONVERSII
O dat are un format extern i un format intern. Prin conversie
nelegem o transformare a unei date dintr-un format al ei n cellalt.
Conversiile se pot face sub controlul unui format sau fr format. Dintre
funciile care realizeaz conversii sub controlul formatelor amintim:
printf;
fprintf;
scanf;
fscanf;
Aceste funcii au fost descrise n leciile anterioare. Vom da
n continuare cteva funcii care realizeaz conversii fr format i care
sunt utilizate mai frecvent. Aceste funcii au prototipurile n fiierul
stdlib.h.
Funcia atoi are prototipul:
int atoi (const char *ptr);
unde:
ptr este un pointer spre o zon de tip caracter ce conine cifre
zecimale care
sunt, eventual, precedate de semnul minus;
Efectul:
irul de cifre spre care pointeaz ptr este convertit din ntreg
zecimal n ntreg binar de tip int.
Observaie:
1o. Funcia returneaz rezultatul acestei conversii.
Funcia atol are prototipul:
long atol (const char *ptr);
unde:
ptr este un pointer spre o zon de tip caracter ce conine cifre
zecimale care
sunt, eventual, precedate de semnul minus;
Efectul:
irul de cifre spre care pointeaz ptr este convertit din ntreg
zecimal n ntreg binar de tip long.
Observaie:
1o. Funcia returneaz rezultatul acestei conversii.
Funcia atof are prototipul:
double atof (const char *ptr);
unde:
ptr este un pointer spre o zon de tip caracter ce conine cifre
zecimale care
sunt, eventual, precedate de semnul minus (poate conine marca
zecimal);
Efectul:
irul de cifre spre care pointeaz ptr este convertit n virgul
flotant dubl precizie.
Observaie:
1o. Funcia returneaz rezultatul acestei conversii.
Funcia itoa are prototipul:
char *itoa (int val, char *sir, int baza)
Efectul:

valoarea parametrului val se convertete din ntreg binar de tip int


n baza de numeraie definit de parametrul baza i se pstreaz n zona
spre care pointeaz sir.
Observaie:
1o. Funcia returneaz pointerul sir.
Funcia ltoa are prototipul:
char *ltoa (long val, char *sir, int baza)
Efectul:
valoarea parametrului val se convertete din ntreg binar de tip long
n baza de numeraie definit de parametrul baza i se pstreaz n zona
spre care pointeaz sir.
Observaie:
1o. Funcia returneaz pointerul sir.
11.4. FUNCII DE PRELUCRARE A IRURILOR DE CARACTERE
Funciile din aceast clas implic includerea fiierului
string.h. Indicm mai jos funciile din aceast clas, utilizate mai
frecvent. O parte din aceste funcii au mai fost utilizate n diferite
exemple din leciile anterioare.
Funcii de copiere:
char *strcpy (char *dest, const char *sursa);
char *strncpy (char *dest, const char *sursa, unsigned n);
prima funcie copiaz irul de caractere spre care pointeaz sursa n
zona spre care pointeaz dest;
a doua funcie realizeaz acelai lucru, dar copiaz cel mult primii
n octei din surs;
ambele funcii returneaz valoarea pointerului dest.
Funcii de concatenare:
int strcmp (const char *dest, const char *sursa);
char *strncat (const char *dest, const char *sursa, unsigned n);
prima funcie copiaz irul spre care pointeaz sursa la sfritul
irului din zona spre care pointeaz dest;
a doua funcie realizeaz acelai lucru, dar se copiaz cel mult
primii n octei din zona spre care pointeaz sursa;
ambele funcii returneaz valoarea pointerului dest.
Funcii de comparare:
int strcmp (const char *sir1, const char *sir2);
int stricmp (const char *sir1, const char *sir2);
int strncmp (const char *sir1, const char *sir2, unsigned n);
int strnicmp (const char *sir1, const char *sir2, unsigned n);
aceste funcii compar irurile de caractere din zonele spre care
pointeaz pointerii sir1 i sir2;
ele returneaz o valoare:
negativ, dac irul spre care pointeaz sir1 este mai mic dect cel
spre care pointeaz sir2;
zero, dac cele dou iruri sunt egale;
pozitiv, dac irul spre care pointeaz sir1, este mai mare dect
cel spre care pointeaz sir2;
prezena literei i (ignore) n numele funciei nseamn c nu se face
distincie ntre literele mari i mici;

prezena literei n n numele


comparaia pe cel mult n octei.

funciei

nseamn

se

realizeaz

Observaie:
1o. Fie irurile s1 i s2 de lungime l1 i l2. Atunci cele dou iruri
sunt egale dac:
l1=l2 ( au aceeai lungime);
s1[k] = s2 [k] pentru k=0,1,...,l1
2o. irul s1 este mai mic dect irul s2 dac exist un j, j 0 i j
min (l1, l2), astfel nct:
s1[j] < s2[j];
s1[k] = s2[k], pentru k=0,1, . . . , j-1.
3o. irul s1 este mai mare dect irul s2 dac exist un j, j 0 i j
(l1, l2), astfel inct:
s1[j] > s2[j];
s1[k] = s2[k], pentru k=0,1, . . . , j-1.
Funcia lungime:
unsigned strlen (const char *sir);
returneaz lungimea irului de caractere spre care pointeaz sir;
caracterul NUL care termin irul nu este numrat.
11.5. FUNCII DE CALCUL
Majoritatea funciilor matematice au prototipurile n fiierul
math.h. Multe dintre acestea se utilizeaz la calculul valorilor funciilor
elementare i au prototipul:
double nume (double x);
unde nume este unul din urmtoarele:
acos -arccos;
asin -arcsin;
atan -arctg;
cos
-cos;
sin
-sin;
exp
-ex;
log
-ln;
log10 -lg;
sqrt -rdcina ptrat;
ceil -returneaz cel mai mare ntreg mai mare sau egal cu x
(partea ntreag);
floor -returneaz cel mai mare ntreg mai mic sau egal cu x;
fabs -valoarea absolut;
sinh -sinus hiperbolic;
cosh -cosinus hiperbolic;
tanh -tangent hiperbolic;
Alte funcii:
double atan2 (double y, double x);
double pow (double x, double y);
double cabs (struct complex z);

- returneaz arctg(y/x);
- returneaz xy;
- returneaz modulul

numrului
complex;
double poly (double x, int n, double c[ ] )
returneaz
valoarea polinomului
de grad n n punctul x,
coeficienii
sunt c[0], . . . c[n].

Funciile care urmeaz au prototipul n fiierele stdlib.h i


math.h:
int abs (int n);

- returneaz valoarea absolut din ntregul

n;
long labs (long n);
ntregul long n;

returneaz

valoarea

absolut

din

11.6. FUNCII PENTRU CONTROLUL PROCESELOR


Aceste funcii au prototipurile n fiierul stdlib.h i n
process.h i realizeaz controale asupra programelor:
void abort (void);
- termin un program n caz de
eroare;
void exit (int stare)
termin
un
program
i
returneaz o stare; stare este
0 pentru terminare normal i diferit
de zero
pentru o terminare anormal; videaz buferele
fisierelor, nchide toate fiierele;
int system (const
definit prin sirul
de caractere spre care
returneaz 0 la succes
Aceste funcii au

char *comanda)

execut

comand

DOS

pointeaz comanda;
si 1 la eroare.
prototipurile n stdlib.h i n process.h.

11.7. FUNCII PENTRU GESTIUNEA DATEI I OREI


Dm
mai
jos
prototipurile
a
patru
funcii
pentru
citirea/setarea datei i orei. Ele implic includerea fiierului dos.h.
void getdate(struct date *d); - ncarc structura de tip date spre
care pointeaz d
cu datele corespunztoare furnizate de sistemul de
operare;
void gettime(struct time *t); - ncarc structura de tip time spre
care pointeaz t
cu datele corespunztoare furnizate de sistemul de
operare;
void setdate (struct date *t);
seteaz
data
curent
n
conformitate cu datele de
tip date;
void settime (struct time *t);
seteaz
ora
curent
n
conformitate cu datele de tip
time spre care pointeaz t.
Tipurile date i time sunt definite n fiierul dos.h. astfel:
struct date {
int da_year;
int da_day;
int da_mon;
};
struct time {
unsigned
unsigned
unsigned
unsigned

char
char
char
char

ti_min;
ti_hour;
ti_hund;
ti_sec;

};
Exemplu:
void main (void)
// afiseaza data si ora
{
struct date d;
struct time t;
getdate (&d);
gettime (&t);
printf (\n\t%02d/%02d/%04d,d.da_day, d.da_mon, d.da_year);
printf (\tora %02d:%02:%02\n, t.ti_hour, t.ti_min, t.ti_sec);
}
11.8. ALTE FUNCII DIVERSE DE UZ GENERAL
void clrscr (void);
prototipul n conio.h
void delay(unsigned i);
perioad de i
milisecunde;
void sleep(unsigned i);
perioad de i
secunde;
void nosound (void);
void sound(unsigned h);
egal cu h Hz.

- terge fereastra activ sau tot ecranul;


-

suspend

execuia

programului

pentru

suspend

execuia

programului

pentru

- oprete difuzorul calculatorului;


- pornete difuzorul calculatorului cu un ton

LECIA 12.
GESTIUNEA ECRANULUI N MOD TEXT
Biblioteca standard a limbajelor C i C++ conine
pentru gestiunea ecranului. Acesta poate fi gestionat n 2 moduri:
modul text i
modul grafic.

funcii

Modul text presupune c ecranul este format dintr-un numr de linii


i coloane. De obicei exist dou variante:
25 de linii x 80 de coloane = 2000 de carctere sau
25 de linii x 40 de coloane = 1000 de caractere.
Poziia pe ecran a unui caracter se definete printr-un sistem de
coordonate ntregi (x,y)
unde:
x - reprezint numrul coloanei n care este situat caracterul;
y - reprezint numrul liniei n care este situat caracterul.
Colul din stnga sus are coordonatele (1,1) iar colul din
dreapta jos (80,25) sau (40,25).
n mod implicit funciile de gestiune a ecranului n mod text
au acces la tot ecranul. Accesul poate fi limitat la o parte din ecran
utiliznd aa numitele ferestre. Fereastra este un dreptunghi care este o
parte a ecranului i care poate fi gestionat independent de restul
ecranului.
Un caracter de pe ecran, pe lng coordonate, mai are i
urmtoarele atribute:
culoarea caracterului afiat;
culoarea fondului;
clipirea caracterului.
Aceste atribute sunt dependente de adaptorul grafic utilizat. Cele
mai utilizate adaptoare sunt:
placa MDA, care este un adaptor monocrom;

placa
placa
placa
placa

HERCULES,
CGA, care
EGA, care
VGA, care

care
este
este
este

este un adaptor color;


un adaptor color;
un adaptor color;
un adaptor color de mare performan.

Pentru adaptoarele de mai sus se pot utiliza 8 culori de fond i 16


culori pentru afiarea caracterelor.
Atributul unui caracter se definete cu ajutorul formulei:
atribut = 16 * culoare_fond + culoare_caracter + clipire

(*)

unde:
culoare_fond (background) = cifr ntre 0 i 7; (8 culori)
culoare_caracter (foreground) = ntreg ntre 0 i 15; (16 culori)
clipire = 128 (clipire) sau 0 (fr clipire)
Tabel cu numele culorilor:
Culoare
negru
albastru
verde
turcoaz
rou
purpuriu
maro
gri deschis
gri nchis
albastru deschis
verde deschis
turcoaz deschis
rou dechis
purpuriu magenta
galben
alb
clipire

Constant simbolic
BLACK
0
BLUE
GREEN
2
CYAN
3
RED
MAGENTA
BROWN
6
LIGHTGRAY
7
DARKGRAY
8
LIGHTBLUE
9
LIGHTGREEN
LIGHTCYAN
11
LIGHTRED
LIGHTMAGENTA
YELLOW
WHITE
15
BLINK

Valoare
1
4
5

10
12
13
14
128

12.1. SETAREA ECRANULUI N MOD TEXT


Setarea (punerea) se realizeaz cu ajutorul funciei textmode care
are prototipul:
void textmode (int modtext);
unde
modtext poate fi exprimat numeric sau simbolic n felul
urmtor:
Modul text activat

Constant simbolic

alb/negru 40 coloane
color 40 coloane
alb/negru 80 coloane
color 80 coloane
monocrom
color 43 linii pentru EGA
i 50 linii pentru VGA
modul precedent

BW40
C40
BW80
C80
MONO
C4350
LASTMODE

Valoare
0
1
2
3

7
64
-1

Modul MONO se poate seta pe un adaptor monocolor. Celelalte


moduri se pot seta pe adaptoare color.
12.2. DEFINIREA UNEI FERESTRE

Dac dorim s partajm ecranul n zone care s poat fi


gestionate independent trebuie s definim ferestre.
O fereastr este o zon dreptunghilar de pe ecran care se
poate defini cu funcia window al crei prototip este:
void window (int stanga, int sus, int dreapta, int jos);
unde:
-(stanga, sus) = coordonatele colului din stnga al ferestrei;
-(dreapta, jos) = coordonatele colului din dreapta jos.
La un moment dat o singur fereastr este activ i anume aceea
definit de ultimul apel al funciei window. Funciile de gestionare a
ecranului n mod text acioneaz ntotdeauna asupra ferestrei active. Dup
setarea modului text cu ajutorul funciei textmode este activ tot ecranul.
Menionm c funcia window nu are nici un efect dac
parametrii de la apel sunt eronai.
12.3. TERGEREA UNEI FERESTRE
Fereastra activ se poate terge cu ajutorul funciei clrscr cu
prototipul:
void clrscr(void);
fereastra activ (sau tot ecranul) devine activ; fondul are culoarea
definit prin culoarea de fond curent;
clrscr poziioneaz cursorul n poziia (1,1) a fiecrei ferestre.
12.4. GESTIUNEA CURSORULUI
Programatorul poate plasa cursorul pe un caracter al ferestrei
folosind funcia gotoxy al crei prototip este:
void gotoxy (int coloana, int linie);
unde:
(coloana, linie) sunt coordonate relative la fereastra activ, dac
sunt nafara ferestrei active apelul este ignorat;
Poziia cursorului se poate determina cu ajutorul funciilor wherex
i wherey care au prototipurile:
int wherex (void);
-returneaz numrul coloanei n
care se afl cursorul
int wherey (void);
-returneaz
numrul
liniei
n
care se afl cursorul.
Exist cazuri cnd se dorete ascunderea cursorului. Acest lucru se
poate realiza printr-o secven special n care se utilizeaz funcia
geninterrupt.
void ascundcursor (void)
// face invizibil cursorul
{ _AH = 1;
_CH = 0x20;
geninterrupt (0x10);
}
Cursorul poate fi reafiat apelnd funcia de mai jos:
void afiscursor (void)
// face vizibil cursorul
{ _AH = 1;
_CH = 6;
_CL = 7;
geinterrupt (0x10);
}
AH, CH, CL reprezint unii din regitrii microprocesorului.

12.5. DETERMINAREA PARAMETRILOR ECRANULUI


Utilizatorul are posibilitatea s obin parametrii cureni ai
ecranului prin apelarea funciei gettextinfo al crui prototip este:
void gettextinfo (struct text_info *p);
unde structura text_info este definit n fiierul conio.h astfel:
struct text_info
{ unsigned char winleft;
unsigned char wintop;
unsigned char winright;
unsigned char winbottom;
unsigned char attribute;
unsigned char normattr;
unsigned char currmode;
unsigned char screenheight;
unsigned char screenwidth;
unsigned char curx;
unsigned char cury;
};

// amplasarea colturilor ferestrei

// culoarea fondului, a caracterelor si


// clipirea
// dimensiunea ecranului
// pozitia cursorului

12.6. MODURILE VIDEO ALB/NEGRU


Exist dou moduri (intens i normal) care se pot activa cu
funciile highvideo i lowvideo ale cror prototipuri sunt:
void highvideo (void);
void lowvideo (void);
Intensitatea iniial este de obicei cea normal. Se poate
reveni la intensitatea normal dac se apeleaz funcia normvideo cu
acelai prototip ca al celorlalte dou funcii.
Exemplu:
Vom scrie un program care afieaz parametrii ecranului:
#include <stdio.h>
#include <conio.h>
void main (void)
{ struct text_info par_ecran;
gettextinfo (&par_ecran)
;
printf (\nstanga: %u sus: %u dreapta: %u jos: %u\n,
par_ecran.winleft,
par_ecran.wintop,
par_ecran.winright,
par_ecran.winbottom);
printf (atribut: %d mod curent: %d\n,
par_ecran.normattr, par_ecran.currmode);
printf (inaltimea ecranului: %d latimea ecrnului: %d\n,
par_ecran.screenheight, par_ecran.screenwidth);
printf (coloana cursorului: %d linia cursorului: %d\n,
par_ecran.curx, par_ecran.cury);
}
12.7. SETAREA CULORILOR
Culoarea
fondului
se
seteaz
textbackground cu prototipul urmtor:
void textbackground (int culoare);

cu

ajutorul

funciei

unde:
culoare este un ntreg cuprins ntre 0 i 7 cu semnificaia din
tabelul anterior al culorilor.
Culoarea caracterelor se seteaz cu ajutorul funciei textcolor
cu prototipul urmtor:
void textattr (int culoare);
unde:
culoare este un ntreg ntre 0 i 15.
Se pot seta ambele culori, precum i clipirea caracterului folosind
funcia textattr de prototip:
void textattr (int atribut);
unde:
atribut e definit cu ajutorul relaiei (*) de la nceputul leciei.

12.8. GESTIUNEA TEXTELOR


Pentru afiarea caracterelor colorate n conformitate cu
atributele definite prin relaia:
atribut = 16 * culoare_fond + culoare_caracter + clipire
se pot folosi funciile:
putch
- pentru afiarea unui caracter;
cputs
- pentru afiarea color a unui ir de caractere (acelai
prototip ca puts);
cprintf
- pentru afiarea color sub controlul formatelor.
Alte prototipuri de funcii:
void insline (void);
- insereaz o linie cu spaii n fereastr,
liniile de sub
poziia cursorului se deplaseaz n jos cu o poziie;
void clreol (void)
- terge sfritul liniei ncepnd cu poziia
cursorului;
void delline (void)
- terge toat linia pe care este poziionat
cursorul;
int movetext ( int stanga, int sus, int dreapta, int jos,
int stanga_dest, int dreapta_dest );
- copiaz un text dintr-o poziie n alta;
returneaz: 1 dac textul s-a copiat cu succes i o n caz de eroare.
Textele dintr-o zon dreptunghiular a ecranului pot fi salvate
sau citite dintr-o zon de memorie cu ajutorul funciilor puttext i
gettext i au prototipurile:
int gettext (int stanga, int sus, int dreapta, int jos, void
*destinatie);
unde
primii patru parametrii definesc fereastra unde se afl textul de
salvat;
destinatie este pointerul spre zona de memorie n care se salveaz
textul.
i
int puttext (int stanga, int sus, int dreapta, int jos, void *sursa);
unde
primii patru parametrii definesc fereastra unde se va scrie pe ecran
textul preluat din memorie;
sursa este pointerul spre zona de memorie din care se transfer
textul.
Ele returneaz:
1 la copiere cu succes;
0 la eroare.

Observaie:
1o. Fiecare caracter de pe ecran se pstreaz pe doi octei:
pe un octet codul caracterului;
pe octetul urmtor atributul caracterului.
Exemple:
Programul urmtor seteaz o fereastr i modurile video alb/negru.
#include <conio.h>
void main (void)
{ textmode (BW80);
window (10,4,60,4);
clrscr ();
lowvideo ();
cputs(lowvideo);
normvideo ();
cputs (normvideo);
textmode (LASTMODE);
cprintf (\n\r Acionai o tast pentru a continua:);
getch ();
}
Programul urmtor afieaz toate combinaiile
pentru fond i caractere (adaptor EGA/VGA).

de

culori

#include <conio.h>
#include <stdio.h>
void main (void)
{ static char *tculoare [ ] = { 0 BLACK
negru,
1
BLUE
albastru,
2
GREEN
verde,
3
CYAN
turcoaz,
4
RED
rosu,
5
MAGENTA
purpuriu,
6
BROWN
maro,
7
LIGHTGRAY gri deschis,
8
DARKGRAY
gri inchis,
9
LIGHTBLUE
albastru deschis,
10
LIGHTGREEN verde deschis,
11
LIGHTCYAN turcoaz deschis,
12
LIGHTRED
rosu dechis,
13
LIGHTMAGENTA
purpuriu magenta,
14
YELLOW
galben ,
15
WHITE
alb};
int i,j,k;
struct text_info atribut;
gettextinfo (&atribut);
for (i = 0; i < 8; i++ )
// i alege culoarea fondului
{ window (3,2,60,20);
k=2;
textbackground (i);
clrscr();
for (j=0; j <10; j++, k++) // j alege culoarea caracterului
{ textcolor (j);
gotoxy (2,k);
if (i = = j) continue;
cputs (tculoare[j]);
}
gotoxy (1,18);
printf (actionati o tasta pentru contiuare\n);
getch();
}
window (atribut.winleft, atribut.wintop, atribut.winright, atribut.winbottom);

posibile

textattr (atribut. attribute);


clrscr();
}
LECIA 13.
GESTIUNEA ECRANULUI N MOD GRAFIC
Modul grafic presupune c ecranul este format din puncte luminoase
(pixeli). Numrul acestora depinde de adaptorul grafic i se numete
rezoluie. O rezoluie este cu att mai bun cu ct este mai mare.
Adaptorul CGA are o rezoluie de 200 rnduri x 640 de coloane iar EGA de
350 de rnduri x 640 de coloane. Adaptorul VGA i SVGA ofer rezoluii n
funcie de mrimea ecranului. Astfel adaptorul VGA i SVGA ofer rezoluii
de pn la 768 de rnduri x 1024 de coloane pentru ecranul de 14 (inches).
Pentru gestiunea ecranului n mod grafic se pot utiliza peste 60 de
funcii standard aflate n biblioteca sistemului. Aceste funcii au
prototiopurile n fiierul graphics.h. Totodat ele folosesc pointeri de
tip far (de 32 de bii).
13.1. SETAREA MODULUI GRAFIC
Modul grafic se seteaz cu ajutorul funciei initgraph. Aceast
funcie poate fi folosit singur sau mpreun cu o alt funcie numit
detectgraph care determin parametrii adaptorului grafic. Prototipul ei
este:
void far detectgraph (int far *graphdriver, int far *graphmode);
unde:
n zona spre care pointeaz graphdriver se pstreaz una
valorile:
valoare

din

simbol

CGA
MCGA
EGA
EGA64
EGAMONO
IBM8514
HERCMONO
ATT400
VGA
PC3270
n zona spre care pointeaz graphmode se memoreaz una din valorile:
pentru CGA
valoare

simbol

CGAC0
CGAC1
CGAC2
CGAC3
corespunznd toate pentru o rezoluie de 320*200 de pixeli i permit
4 culori
CGAHI
are o rezoluie de 640*200 puncte i lucreaz numai alb/negru.
pentru EGA
valoare

simbol

EGALO are 640*200 i 16 culori


EGAHI are 640*350
pentru VGA

valoare

simbol

0
1
2

VGALO are 640*200


VGAMED
are 640*350
VGAHI are 640*480

Valorile spre care pointez graphdriver definesc nite funcii


standard corespunztoare adaptorului grafic. Aceste funcii se numesc
drivere. Ele se afl n subdirectorul BGI.
Funcia detectgraph detecteaz adaptorul grafic prezent la calculator
i pstreaz valoarea corespunztoare acestuia n zona spre care pointeaz
graphdriver. Modul grafic se definete n aa fel nct el s fie cel mai
performant pentru adaptorul grafic curent. Cele mai utilizate adaptoare
sunt de tip EGA (calculatoare mai vechi) i VGA i SVGA (calculatoarele mai
noi).
Apelul funciei detectgraph trebuie s fie urmat de apelul funciei
initgraph. Aceasta seteaz modul grafic n conformitate cu parametrii
stabilii de apelul prealabil al funciei detectgraph.
Funcia initgraph are prototipul:
void far initgraph (int far * graphdriver, int far *graphmode, char
far *cale);
unde:
graphdriver i graphmode sunt pointeri cu aceeai semnificaie ca n
cazul funciei detectgraph;
cale este un pointer spre irul de caractere care definesc calea
subdirectorului BGI (Borland Graphic Interface), de exemplu:
C:\\BOLANDC\\BGI
Exemplu:
Setarea n mod implicit a modului grafic:
int driver, mod_grafic;
...
detectgraph (&driver, &mod_grafic);
initgraph(&driver, &mod_grafic, C:\\BORLANDC\\BGI);
Dup apelul funciei initgraph se pot utiliza celelalte funcii
standard de gestiune grafic a ecranului.
Din modul grafic se poate iei apelnd funcia closegraph care
are prototipul:
void closegraph (void);
Funcia initgraph mai poate fi apelat i folosind constanta
simbolic DETECT astfel:
int driver, mod_grafic;
...
driver = DETECT;
initgraph (&driver, &mod_grafic, C:\\BORLANDC\\BGI);
Constanta
simbolic
DETECT
este
definit
n
fiierul
graphics.h, alturi de celelalte constante simbolice care definesc
driverul. Aceasta are valoarea zero.
Prin apelul de mai sus, funcia initgraph apeleaz funcia
detectgraph pentru a defini parametrii implicii ai adaptorului grafic.
Utilizatorul
poate
defini
el
nsui
parametri
pentru
iniializarea modului grafic.
13.2. GESTIUNEA CULORILOR
Adaptoarele grafice sunt prevzute cu o zon de memorie n care
se pstreaz date specifice gestiunii ecranului. Aceast zon de memorie
poart denumirea de memorie video.

n mod grafic, ecranul se consider format, dup cum am precizat, din


puncte luminoase numite pixeli. Poziia pe ecran a unui pixel se definete
printr-un sistem de coordonate (x,y) cu x coloana i y linia n care este
afiat pixelul.
n cazul adaptoarelor color, unui pixel i corespunde o culoare.
Culoarea pixelilor se pstreaz pe bii n memoria video. Memoria video
necesar pentru a pstra starea ecranului setat n mod grafic, se numete
pagin video. Adaptoarele pot conine mai multe pagini video. Gestiunea
culorilor este dependent de tipul de adaptor grafic existent la
microprocesor.
Numrul maxim de culori pentru adaptoarele EGA este de 64 (numerotate
de la 0 la 63). Cele 64 de culori nu pot fi afiate simultan pe ecran. n
cazul adaptorului EGA se pot afia simultan 16 culori. Mulimea culorilor
care pot fi afiate simultan pe ecran se numete palet. Culorile din
componena unei palete pot fi modificate de utilizator prin intermediul
funciilor standard. La iniializarea modului grafic se seteaz o palet
implicit.
Pentru adaptorul EGA exist un tablou de 64 de culori (cu coduri
ntre 0 i 63) din care se selecteaz cele 16 culori pentru palet. Exist
i constante simbolice foarte sugestive cu numele culorilor n englez.
Funciile de gestiune a culorilor pot avea ca parametri nu numai codurille
culorilor ci i aceste constante simbolice.
Culoarea fondului este ntotdeauna cea corespunztoare indicelui 0
din palet. Culoarea pentru desenare este cea corespunztoare indicelui 15.
Culoarea de fond poate fi modificat cu ajutorul funciei setbkcolor
care are prototipul:
void far setbkcolor (int culoare);
unde:
culoare = index n tabloul care definete paleta.
Exemplu:

setbkcolor (BLUE); seteaz culoarea de fond pe albastru.

Pentru a cunoate culoarea


funcia getbkcolor de prototip:
int far getbkcolor (void);

de

fond

curent

se

poate

apela

Ea returneaz indexul n tabloul care definete paleta pentru


culoarea de fond.
Culoarea pentru desenare poate fi modificat folosind funcia
setcolor de prototip:
void far setcolor(int culoare);
unde:
culoare = index n tabloul care definete paleta.
Exemplu:
galben.

setcolor (YELLOW); seteaz culoarea pentru desenare n

Culoarea pentru desenare


getcolor de prototip:
int far getcolor (void);

se

poate

determina

apelnd

funcia

Ea returneaz indexul n tabloul care definete paleta relativ


la culoarea pentru desenare.
Paleta curent poate fi modificat cu funciile setpalette i
setallpalette. Prima funcie se folosete pentru a modifica o culoare din
paleta curent i are prototipul:
void far setpalette (int index, int cod);
unde:
index este un ntreg din {0,. . . , 15} i reprezint indexul n
tabloul care definete paleta pentru culoarea care se modic;

cod este un ntreg din intervalul {0, 1,. . . , 63} i reprezint


codul culorii care o nlocuiete n palet pe cea veche.
Exemplu:
setpalette (DARKGRAY, 45);
corespunztoare
indicelui DARKGRAY (adic 8) prin
culoarea de cod 45.

modific

culoarea

Funcia setallpalette permite modificarea


multor culori din compunerea paletei i are prototipul:

simultan

mai

void far setallpalette (struct palettetype far *paleta);


unde:
palettetype este un tip definit n fiierul graphics.h astfel
struct palettetype
{ unsigned char size;
unsigned char colors [MAXCOLOR+1];
};
cu
- size dimensiunea paletei
colors tablou ale crui elemente au ca valori codurile componente ale
paletei care se definete.
Modificarea paletei curente cu ajutorul funciilor setpalette
i setallpalette conduce la schimbarea corespunztoare a culorilor afiate
pe ecran n momentul apelului funciilor respective.
Pentru a determina codurile culorilor componente ale paletei
curente se va folosi funcia getpalette de prototip:
void far getpalette (struct palettetype far *paleta);
Paleta
implicit
poate
fi
determinat
getdefaultpalette de prototip:
struct palettetype *far getdefaultpalette(void);
Numrul culorilor dintr-o
funcia getmaxcolor de prototip:
int far getmaxcolor (void);

palet

poate

folosind

fi

obinut

funcia

apelnd

Funcia returneaz numrul maxim de culori diminuat cu 1. Deci


n cazul adaptorului EGA funcia returneaz valoarea 15.
O alt funcie care determin dimensiunea paletei este funcia
getpalettesize cu prototipul:
int far getpalettesize (void);
Funcia returneaz numrul culorilor componente ale paletei. n
cazul adaptorului EGA funcia returneaz valoarea 16.
Exemplu:
Programul
implicit:

urmtor

afieaz

codurile

#include <graphics.h>
#include <stdlib.h>
#include <stdio.h>
#include <conio.h>
void main (void)
{ int gd = DETECT, gm, i;
struct palettetype far *pal = (void *) 0;
initgraph (&gd, &gm, C:\\BORLANDC\\BGI);
pal = getdefaultpalette ();
for (i=0; i<16; i++)

culorilor

pentru

paleta

{ printf (colors[%d]=%d\n, i, pal -> colors[i]);


getch ();
}
closegraph();
}
13.3. STAREA ECRANULUI
n mod grafic ecranul se compune din n*m pixeli. Pixelul din
stnga-sus are coordonatele (0,0), iar pixelul din dreapta-jos are
coordonatele (n-1,m-1).
n BGI exist mai multe funcii care permit utilizatorului s
obin informaii despre:
coordonata maxim pe orizontal;
coordonata maxim pe vertical;
poziia curent (pixelul curent);
etc.
int far getmaxx (void); returneaz abscisa maxim;
int far getmaxy (void); returneaz ordonata maxim;
int far getx (void);
returneaz
poziia
(abscisa) pixelului curent;
int far gety (void);
returneaz
poziia
(ordonata) pixelului curent.

pe
pe

orizontal
vertical

Exemplu:
#include <graphics.h>
#include <stdlib.h>
#include <stdio.h>
#include <conio.h>
void main (void)
{ int gd = DETECT, gm, cul_fond, cul_desen, curent_x,curent_y, maxx, maxy;
initgraph (&gd, &gm, C:\\BORLANDC\\BGI);
cul_fond = getbkcolor();
cul_desen = getcolor();
maxx = getmaxx();
maxy = getmaxy();
curent_x = getx();
curent_y = gety();
closegraph();
printf (culoarea fondului = %d\n, cul_fond);
printf (culoare desenare = %d\n, cul_desen);
printf (abscisa maxima = %d\n, maxx);
printf (ordonata maxima = %d\n, maxy);
printf (abscisa curenta = %d\n, curent_x);
printf (ordonata curenta = %d\n, curent_y);
printf (acionai o tasta pentru terminare);
getch();
}
LECIA 14.
PROBLEME DIVERSE
De multe ori suntem pui n faa unor probleme pe care le
nelegem uor dar nu tim cum s le rezolvm ct mai simplu i elegant. V
propunem cteva metode care bine nsuite pot duce, uneori, la o rapid
rezolvare a problemelor dificile. Evident c, nu toate problemele pot fi
ncadrate n aceste tipare propuse dar fiecare programator poate s-i
formeze un astfel de "portofoliu" de metode cu care s poate aborda orice
problem. Metodele prezentate n continuare pot constitui un nceput.
14.1. GENERAREA COMBINRILOR

Fie o mulime oarecare de n elemente care poate fi pus ntr-o


coresponden biunivoc cu mulimea A={1,...,n}. Ne propunem s determinm
toate m-combinrile (mn) ale mulimii A (submulimi de m elemente ale
mulimii A). Vom rezolva problema, nerecursiv, pornind de la m-combinarea
V=(1,2,...,m) determinnd succesiv toate m-combinrile ordonate lexicografic
cresctor.
Fie V=(v1,v2,...,vm) o m-combinare oarecare, atunci m-combinarea
urmtoare n ordine lexicografic cresctoare se obine astfel:
se determin cel mai mare indice i satisfcnd relaiile:
vi<n-m+i, vi+1=n-m+i+1,..., vm-1=n-1, vm=n. (1)
se trece la vectorul urmtor:
(v1,...,vi-1,vi+1,vi+2,...,vi+n-i+1);
dac nu exist un astfel de indice i (care s satisfac relaiile (1))
nseamn c vectorul V conine ultima m-combinare i anume: (n-m+1,n-m+2,
...,n).
Dm n continuare o funcie C care genereaz o m-combinare cu n
elemente avnd ca parametru cod o variabil boolean care pentru valoarea 0
genereaz prima m-combinare iar pentru valoarea 1 genereaz urmtoarea mcombinare. Funcia comb rentoarce valoarea 1 dac s-a generat o m-combinare
oarecare i valoarea 0 dac s-a generat ultima m-combinare (n acest caz cod
are valoarea 0). Se va folosi un vector global v n care se genereaz mcombinrile.
#define dim 50
#include <stdio.h>
int v[dim+1], n, m;
// functia generatoare a m-combinarilor
int comb(cod)
int cod;
{
int i,j;

// generarea primei m-combinari 1,...,m


if (cod == 0)
{
for (i=1; i<=m; v[i]=i++);
return (1);
}
i=m+1;
// cautarea indicelui i pentru satisfacerea relaiilor (1)
do { i-- }
while (v[i] >= n-m+i);
if (i)
{
v[i]=v[i]+1;
for (j=i+1; j<=m; v[j]=v[j-1]+1,j++);
return (1);
}
else return (0);
}
void main(void)
{
int kod,i;
printf("\ndati n: ");
scanf ("%d",&n);

printf("\ndati m: ");
scanf ("%d",&m);
comb(0);
kod=1;
while (kod)
{ printf("\n");
for (i=1;i<=m;printf ("%3d",v[i++]));
kod = comb(kod);
}
getche();
}
14.2. METODA GREEDY
Se aplic problemelor n care se d o mulime A coninnd n date
(de orice natur i structur) de intrare cerndu-se s se determine o
submulime B (BA) care s ndeplineasc anumite condiii pentru a fi
acceptat. Cum, n general, exist mai multe astfel de submulimi (numite
soluii posibile) se mai d i un criteriu conform cruia dintre aceste
submulimi s alegem una singur (numit soluie optim) ca rezultat final.
Foarte multe probleme de cutare se nscriu n acest tip.
Menionm c, n general, soluiile posibile au urmtoarele
proprieti:
- se presupune c este soluie posibil;
- dac B este soluie posibili CB atunci i C este soluie
posibil;
Vom da n continuare o variant a tehnicii greedy (denumire care
n traducere nseamnn lcomie, nghiire) n care se pornete de la mulimea
vid. Apoi se alege pe rnd, ntr-un anumit fel, un element din A neales
nc. Dac adugarea lui la soluia parial anterior construit conduce la o
soluie posibil, atunci se adaug, altfel se alege un nou element. Tot acest
procedeu se repet pentru toate elementele din A. Dm n continuare n
pseudocod o procedur:
procedure GREEDY (n,A,B)
B:=;
for i=1,n do
call ALEGE (A,i,x);
call POSIBIL (B,x,cod);
if cod=1 then
call ADAUG (B,x);
endif;
endfor;
return;
end procedure
Despre procedurile apelate din GREEDY precizm urmtoarele:
procedura ALEGE furnizeaz n x un element din A aj{ai,...,an} i
interschimb ai cu aj; dac la fiecare pas se cerceteaz ai atunci procedura
se simplific;
procedura POSIBIL verific dac elementul x poate fi adugat sau nu
mulimii pariale construit pn la pasul curent furniznd o valoare
boolean cod cu semnificaia:
cod = 1, dac B U {x}, este soluie posibil
cod = 0, altfel
procedura ADAUG nlocuiete pe B cu B{x}.
Obs. Procedurile de mai sus nu sunt necesare ntotdeauna, acest fapt
depinznd de complexitatea problemei. Oricum trebuie reinut cadrul de
rezolvare al acestui tip de problem.
Problem rezolvat

Se d o mulime de valori reale X={x1,


submulimea YX astfel ca y /yY, s fie maxim.

.,xn}.

Se

cere

Evident c problema este foarte simpl (n Y trebuie introduse


elementele strict pozitive din X; evitm s mai construim procedurile ALEGE,
POSIBIL, ADAUG) i vom da rezolvarea ei n pseudocod:
procedure SUMA (n,X,Y,k)
integer n,X(n),Y(n),k
k:=0;
for i=1,n do
if x(i) > 0 then k:=k+1;
y(k):=x(i)
endif;
endfor;
return;
end procedure
Probleme propuse
14.2.1.
Se dau n iruri S1,S2,...,Sn ordonate cresctor, de lungimi L1,L2,
...,Ln. S se obin un ir S de lungime L1+L2+...+Ln cu toate elementele
celor n iruri ordonate cresctor (problem de interclasare).
Indicaie: Vom interclasa succesiv cte dou iruri n final obinnd
irul ordonat cresctor. Complexitatea interclasrii a dou iruri A i B de
lungimi a i b depinde direct proporional de a+b (pentru c se fac a+b
deplasri de elemente). Se pune problema gsirii ordinii optime n care
trebuie efectuate aceste interclasri astfel ca numrul total de deplasri s
fie minim.
Vom da un exemplu pentru a lmuri mai bine lucrurile:
fie 3 iruri de lungimi (90,40,10). Interclasm irul S1 cu S2 apoi
irul rezultat cu S3; putem nota acest lucru formal prin (S1+S2)+S3. Se obin
(90+40) + (130+10)=270 deplasri. Dac vom interclasa irurile n ordinea
(S2+S3)+S1 vom obine (40+10)+ (50+90)=190 de deplasri. De aici concluzia c
ntotdeauna vom interclasa irurile de lungimi minime din irurile rmase.
14.2.2.
Gsii tripletele de numere pitagorice din Nn x Nn x Nn (prin Nn notat
mulimea {0,1,2,...,n}). ncercai optimizarea timpului de execuie a
programului.
14.2.3.
Fie mulimea m-combinrilor luate din n elemente i fie k<m un numr
natural. S se dea un algoritm i apoi s se scrie un program C astfel nct
s se determine o submulime de m-combinri cu proprietatea c oricare dou
m-combinri au cel mult k elemente comune.
14.2.4.
S se determine mulimile interior stabile (MIS) ale unui graf oarecare
dat prin matricea sa de adiacen.
14.3. METODA BACKTRACKING (CUTARE CU REVENIRE)
Aceast metod se aplic problemelor ce pot fi reprezentate sub
forma unui arbore finit iar cutarea soluiei presupune parcurgerea arborelui
n adncime (DF=Depth First).
Problemele de acest tip au n general soluia de forma x=(x1, . . . ,xn)
S1x . . . xSn, fiecare Sk fiind o mulime finit. Mai facem cteva precizri
preliminare:
a) pentru fiecare problem sunt date anumite relaii ntre componentele
x1, . . . ,xn ale lui
x numite condiii interne;

b) produsul cartezian S=S1x...xSn se mai numete spaiul soluiilor


posibile, iar soluiile
posibile care satisfac condiiile interne se numesc soluii rezultat;
c) n general se cer dou lucruri n astfel de probleme:
- determinarea tuturor soluiilor rezultat;
- determinarea doar a acelor soluii care optimizeaz o funcie
obiectiv dat.
Cum rezolvm astfel de probleme? Exist dou modaliti de
rezolvare:
1) tehnica direct (numit i tehnica forei brute) prin care se
genereaz toate elementele spaiului de soluii posibile i apoi se verific
fiecare dac este sau nu o soluie rezultat; aceast tehnic necesit un timp
prohibitiv (dac fiecare Si are doar 2 componente complexitatea este O(2n);
totodat ar fi necesar imbricarea a n cicluri cu n aprioric necunoscut).
2) backtracking care evit generarea tuturor soluiilor posibile.
S dm n continuare cteva repere ale rezolvrii:
soluia este construit progresiv, component cu component;
lui xk i se atribuie o valoare (evident c numai din Sk) dac i numai
dac x1, . . . ,xk-1 au deja valori;
se verific condiiile de continuare (strns legate de condiiile
interne) dac are sens trecerea la xk+1;
dac nu are sens (adic condiiile de continuare nu sunt ndeplinite
atunci facem o nou alegere pentru xk sau dac am epuizat valorile din Sk
atunci k:=k-1 i se face o nou alegere pentru xk-1; .a.m.d.
2. dac are sens atunci k:=k+1 i se testeaz dac k>n:
21) dac inegalitatea este adevrat atunci se afieaz soluia
astfel determinat
i k:=k-1 continund procesul de la punctul 1;
22) dac inegalitatea este fals se continu procesul de la
punctul 1.
Procedura rezolvrii unor astfel de probleme este:
procedure BT(n,x)
integer n;
array x(n);
k:=1;
while k>0 do
cod:=0;
while ([mai exist o valoare din Sk netestat] and cod=0)
x(k):=;
if k(x(1),...,x(k)) then cod:=1;
endif;
endwhile;
if cod=0 then
k:=k-1
else
if k=n then write (x(1),...,x(n))
else k:=k+1
endif
endif;
endwhile;
return;
end procedure
Vom face cteva precizri:
1o. Predicatul k(x1, . . . , xk) reprezint condiiile de continuare
pentru x1, . . . , xk;
2o.
Cod
este
o
valoare
ce
indic
ndeplinirea/nendeplinirea
condiiilor de continuare;
3o. Dac predicatul k(x1, . . . , xk) este adevrat k {1,...,n}
atunci se vor afla toi vectorii din S;
4o. Backtracking poate fi uor reprezentat pe un arbore construit
astfel:

- nivelul 1 conine rdcina;


- din orice nod de pe nivelul k pleac sk
etichetate cu cele
sk elemente ale lui Sk;
- nivelul n+1 va conine s1*s2*. . .* sn
- pentru fiecare nod de pe nivelul
coninute n drumul ce
leag rdcina de acest nod reprezint
Dac
mulimile
Sk
reprezint
algoritmul general se modific astfel:

muchii spre nivelul k+1,


noduri frunz;
n+1 etichetele muchiilor
o soluie posibil;

progresii

aritmetice

atunci

procedure BTA(n,a,b,h)
integer n;
array primul(n),ultimul(n),ratia(n),x(n);
k:=1;
x(1):=primul(1)-ratia(1);
while k>0 do
cod:=0;
while ( x(k)+ratia(k) ultimul(k) and cod=0 )
x(k):=x(k)+ratia(k);
if k(x(1),...,x(k)) then cod:=1 endif;
endwhile;
if cod=0 then
k:=k-1
else
if k=n then write (x(1),...,x(n))
else k:=k+1
x(k):=a(k)-h(k)
endif
endif;
endwhile;
return;
end procedure
unde:
- primul(n) reprezint vectorul primilor termeni ai progresiilor
aritmetice;
ultimul(n)
reprezint
vectorul
ultimilor
termeni
ai
progresiilor aritmetice;
- ratia(n) reprezint vectorul raiilor progresiilor aritmetice;
De
evitarea
(n algoritmul
evitarea
xSn.

reinut cele dou avantaje ale acestui procedeu:


imbricrii unui numr oarecare de cicluri aprioric variabil
propus se imbric doar dou cicluri pretestate while);
construirii spaiului tuturor soluiilor posibile S1xS2x . . .

Problem rezolvat
n cte moduri se pot aranja 8 dame pe tabla de ah astfel nct s nu
se "bat" reciproc. S se foloseasc al doilea algoritm dintre cei menionai
anterior.
Prima variant
Acest program respect algoritmul anterior cu unele mici
modificri. Facem precizarea c vectorul x conine n componenta x[i] numrul
coloanei de pe tabla de ah pe care se va afla dama de pe linia i. Tiprirea
va reprezenta o permutare (din cele 8! soluii posibile). Se vor afla 92 de
soluii. Lsm pe seama cititorului s deduc analogiile i diferenele ntre
algoritm i program.

#include <stdio.h>
#include <math.h>
void main (void)
{ int x[9],cod,k,i,nr;
k=1; x[1]=0;nr=0;
while (k>0)
{ cod=0;
while (((x[k]+1)<=8)&&(cod= =0))
{ x[k]++;
if ((k= =1) && (x[k]= =1)) cod=1;
else { i=1; cod=1;
while ((cod==1)&&(i<k))
{ if ((x[i]==x[k])||(abs(x[i]-x[k])==k-i)) cod=0;
i++;
}
}
}
if (cod= =0) k--;
else {if (k= =8)
{ printf("\n%3d. ",++nr);
for (i=1;i<9;printf("%d ",x[i++]));
}}
else x[++k]=0;
}
}
}
A doua variant:
#include <stdio.h>
#include <math.h>
#define n 100
int x[100],cod,k,nc,nsol;
int Verifica(void)
{ int i,cod1;
// cod1=1 conditiile de continuare
cod1=1;
// sunt verificate
if (k>1)
// cod1=0 in caz contrar;
for(i=1; i<= (k-1); i++)
{ if (x[k]= =x[i]) cod1=0;
if (abs(x[k]-x[i]) = = k-i) cod1=0; // (*)
}
return cod1;
}
void ScrieSolutie(void)
{ int i;
printf("\n%3d. ",++nsol);
for (i=1; i<=nc; printf("%3d",x[i++]));
}
void CitesteDate(void)
{ int i;
nsol=0;
clrscr();
nc=n+1;
while ((nc>n) || (nc<0))
{ printf ("Dati n = ");
scanf ("%d",&nc);
};
}
void main (void)

{ CitesteDate();
k=1; x[1]=0;
while (k>0)
{ cod=0;
while (((x[k]+1) <= nc) && (cod= =0))
{x[k]++;
cod=Verifica();
}
if (cod= =0) k--;
else {if (k==nc) ScrieSolutie();
else x[++k]=0;
}
}
}
A doua variant este modular, mai uor de neles i
generalizeaz problema damelor pn la tabla de ah de 100x100. Lsm pe
seama cititorului modificarea funciei ScrieSolutie pentru a afla n mod
grafic tabla de ah.
Dac n funcia Verifica se terge instruciunea notat cu (*)
atunci se obin toate permutrile de n obiecte.
Probleme propuse
14.3.1
S se rezolve problema turelor de ah dup al doilea algoritm. n cte
moduri se pot aranja n turnuri pe tabla de ah astfel nct s nu se "bat"
reciproc.
14.3.2.
S se afieze poziiile succesive ale unui cal pe tabla de ah, pornind
dintr-o poziie dat, astfel nct s fie atinse toate csuele tablei de
ah.
14.3.3.
Avnd un fiier cu o mulime de cuvinte din limba romn de aceeai
lungime k s se scrie un program C care afieaz toate careurile rebusiste
fr puncte negre. ( Problema e fascinant implicnd i cunotine
gramaticale dar i cunoscnd faptul c nu s-au construit careuri de 10x10
fr puncte negre manual i nici cu ajutorul calculatorului; se poate ncerca
apoi i cu k:=11,12, . . .).
14.3.4.
Un intreprinztor dispune de un capital C i are n variante de
investiii. Pentru fiecare investiie i cunoate fondurile de investiie f i
precum i beneficiile bi. Se cere un program care s deduc toate variantele
posibile de investiii al intreprinztorului. Se mai dau i condiiile Cci
i {1, . . . ,n}.
14.3.5.
Avnd un graf neorientat caracterizat prin matricea costurilor s se
determine prin bactraking circuitul de cost minim pornind dintr-un vrf dat.
14.3.6.
Avnd un graf neorientat caracterizat prin matricea de inciden a
vrfurilor s se determine prin bactraking mulimile interior stabile
maximale.
14.3.7.
S se determine toate cuvintele binare de lungime 10 care conin exact
5 cifre de 1.
14.3.8.

S se determine toate cuvintele binare de lungime 10 care conin cel


mult 5 cifre de 1.
14.3.9.
S se determine toate cuvintele din {a,b,c}* (mulimea tuturor
cuvintelor peste alfabetul se noteaz cu * ) de lungime 10 care conin
exact 2 simboluri 'a'; 3 simboluri 'b' i 5 simboluri 'c'.
14.3.10.
S se determine toate cuvintele din {a,b,c}* de lungime n care conin
exact na simboluri 'a'; nb simboluri 'b' i nc simboluri 'c' (cu condiia
n=na+nb+nc).
14.3.11.
S se determine toate tripletele (x1,x2,x3) de numere astfel ca:
x1+x2+x3suma
x1*x2*x3produs
cu valorile suma i produs date iar x1S1, x2S2 i x3S3 ; S1, S2 i S3
fiind trei progresii aritmetice date deasemenea.
14.3.12.
S se determine toate variantele de pronosport cu 13 rezultate din
{1,x,2} care conin exact n1 simboluri '1'; nx simboluri 'x' i n2 simboluri
'2' (cu condiia n1+nx+n2=13).
14.3.13.
S se determine toate variantele de pronosport cu 13 rezultate din
{1,x,2} care conin cel mult n1 simboluri '1'; cel mult nx simboluri 'x' i
simboluri '2' n rest (cu condiia n1+nx13).
14.4. METODA DIVIDE ET IMPERA (DIVIDE I STPNETE)
Aceast modalitate de elaborare a programelor const n
mprirea repetat a unei probleme de dimensiune mai mare n dou sau mai
multe
subprobleme
de
acelai
tip
urmat
de
combinarea
soluiilor
subproblemelor rezolvate pentru a obine soluia problemei iniiale.
Se d un vector A=(a1,...,an) i trebuie efectuat o prelucrare
oarecare asupra elementelor sale.
Presupunem c:
p,q{1,...,n} cu 1 p < q m{p,p+1,...,q-1} a.. prelucrarea
secvenei {ap,...,aq} se poate face prelucrnd subsecvenele:
{ap,...,am} i {am+1,...,aq} i apoi combinnd rezultatele pentru a
obine prelucrarea ntregii secvene {ap,...,aq}.
Dac se reuete o astfel de formalizare a problemei atunci ea poate fi
rezolvat cu ajutorul acestei metode.
Vom da n continuare o procedur recursiv n pseudocod:
procedure DI (p,q,)
if q-p eps then
call PREL (p,q,)
else
call IMPARTE (p,q,m) ;
call DI (p,m,);
call DI (m+1,q,);
call COMB (,,);
endif;
return;
end procedure
Cteva precizri se impun:
procedura trebuie apelat
rezultatul final;

prin

call

DI

(1,n,)

obinndu-se

eps este lungimea maxim a unei secvene {ap,...,aq} notat prin (p,q)
pentru care se face prelucrarea direct fr a mai fi necesar mprirea n
subprobleme;
procedura PREL realizeaz prelucrarea direct a secvenelor (p,q);
procedura COMB realizeaz combinarea rezultatelor i ale
prelucrrii a dou secvene vecine (p,m) i (m+1,q) obinnd rezultatul
al prelucrrii ntregii secvene (p,q);
prin procedura IMPARTE se obine valoarea lui m.
Vom da ca exemplu problema sortrii cresctoare a unui ir de
ntregi prin interclasare.
deoarece secvenele (i,i+1) sunt uor de ordonat acestea vor constitui
secvenele ce se vor prelucra, deci eps = 1;
- m se va calcula ca (p+q)/2, deci nu mai e nevoie de procedura
special IMPARTE;
procedura COMB va interclasa ntotdeauna dou secvene (p,m) i (m+1,q)
ordonate cresctor;
vom folosi un vector x drept structur global i vom face toate
prelucrrile pe elementele sale nemaiavnd nevoie de zonele ,;
pentru zona vom folosi un vector local y n procedura COMB acesta
coninnd elementele corespondente din x dar ordonate cresctor; tot n
procedura COMB se vor copia apoi elementele lui y din poriunea (p,q) n x;
evident c procedurile din schema general a algoritmului sunt funcii
C cititorul fcnd analogiile necesare.

#include <stdio.h>
#include <conio.h>
#define nrelem 100
int x[nrelem];
int n;
void PREL (int p, int q)
{int aux;
if ((p<q) && (x[p] > x[q])) {
aux=x[p];
x[p]=x[q];
x[q]=aux;
}
}
void COMB (int inf, int mijloc, int sup)
{int i,j,k,l;
int y[nrelem];
i=k=inf;
j=mijloc+1;
while (i<=mijloc && j<=sup)
{ if (x[i] <= x[j])
y[k++]=x[i++];
else
y[k++]=x[j++];
}
for(l=i; l<=mijloc; y[k++]=x[l++]);
for(l=j; l<=sup; y[k++]=x[l++]);
for(k=inf; k<=sup; x[k++]=y[k]);
}

void DI (int p, int q)


{ int m;
if ((q-p) <= 1)
PREL (p,q);
else
{m=(p+q)/2;

DI (p,m);
DI (m+1,q);
COMB (p,m,q);
}
return;
}
void main(void)
{int i;
clrscr();
printf ("dati nr de elemente\n");
scanf ("%d",&n);
for (i=1; i<=n; i++)
{printf("x[%d]=",i);
scanf("%d",&x[i]);
}
DI (1,n);
printf("\nsirul sortat crescator este:\n");
for (i=1; i<=n; i++) printf("x[%d]=%d\n",i,x[i]);
getch();
}
14.5. METODA PROGRAMRII DINAMICE
Aceast metod este aplicabil problemelor de optim n care
soluia poate fi privit ca rezultatul unui ir de decizii d1, . . . ,dn.
Fiecare decizie depinde de deciziile deja luate i nu este unic determinat
(spre deosebire de tehnica greedy unde fiecare decizie care se ia trebuie s
fie unic). Totodat este necesar s fie satisfcut una din variantele
principiului optimalitii pentru a putea fi aplicat aceast metod.
Vom formaliza acest principiu al optimalitii:
fie d1,...,dn un ir optim de decizii (SOD) care transform starea so n
starea sn, trecnd prin strile intermediare s1, . . . ,sn-1; vom nota acest
fapt prin (d1,dn) este SOD pentru perechea de stri (so,sn);
grafic procesul este descris ca n figura urmtoare:
d1
d2
dn
*----->*---->*------>* . . . *------>*
so
s1 s2
sn-1 sn
Vom da acum mai multe variante ale principiului optimalitii:
dac (d1,dn) este SOD pentru (so,sn) atunci (d2,dn) este SOD pentru
(s1,sn);
2) dac (d1,dn) este SOD pentru (so,sn) atunci i din {1, . . . ,n-1}
avem
a) (d1,di) este SOD pentru (so,si) SAU
b) (di+1,dn) este SOD pentru (si,sn).
3) dac (d1,dn) este SOD pentru (so,sn) atunci i din {1, . . . ,n-1}
avem
a) (d1,di) este SOD pentru (so,si) I
b) (di+1,dn) este SOD pentru (si,sn).
Ultima variant este cea mai general i mai complet. Odat
verificat o form a principiului optimalitii, metoda programrii dinamice
const n a scrie relaiile de recuren i apoi de a le rezolva. n general
relaiile de recuren sunt de 2 tipuri :
fiecare decizie di depinde de di+1,...,dn - relaii de recuren nainte,
deciziile vor fi luate n ordinea dn, dn-1, . . . ,d1;
fiecare decizie di depinde de d1, . . . ,di-1 - relaii de recuren
napoi, deciziile vor fi luate n ordinea d1, d2, . . . , dn.

Problem rezolvat
Fie G=(X,) un 1-graf orientat cruia i atam matricea
costurilor, (fiecare arc (i,j) este etichetat cu o valoare real strict
pozitiv). Se pune problema gsirii drumului de valoare minim (DVM) ntre
oricare 2 vrfuri i i j.
Rezolvare
Verificm prima variant a principiului optimalitii:
- fie i, i1, i2, . . . , im, j DVM ntre i i j atunci evident c i1, .
. . , im, j este DVM ntre i1 i
j;
- notm prin L(i,k) lungimea DVM dintre i i k, kX;
- notm deasemenea dkj valoarea arcului (k,j);
- ecuaiile de recuren sunt:
L(i,j) = min {L(i,k) + dkj)}
(k,j)
#include<stdio.h>
#include<values.h>
#define nn 10
int d[nn+1][nn+1],i,j,n;
int L(int i,int j)
{
int m=MAXINT;
int k,x=0;
if (i= =j) m=0;
else
for (k=1;k<=n;k++)
if (d[k][j] < MAXINT)
{ x=L(i,k)+d[k][j];
if (x<m) m=x;
}
return m;
}
void citestematrice(void)
{int i,j;
printf("\ndati dimensiunea matricii distantelor : ");
scanf("%d",&n);
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
{ printf("d[%d,%d]= ",i,j);
scanf ("%d",&d[i][j]);
}
for(i=1;i<=n;i++)
for(j=1;j<=n;j++) if (d[i][j]= = -1) d[i][j]=MAXINT;
}
void citeste(void)
{ printf("\ndati varful initial : ");
scanf("%d",&i);
printf("\ndati varful final : ");
scanf("%d",&j);
}
void afiseaza(int val)
{ printf("\nvdlm intre varful %d si %d este %d",i,j,val);

}
void main(void)
{ int vdlm;
clrscr();
citestematrice();
citeste();
vdlm=L(i,j);
afiseaza(vdlm);
getch();
}
Probleme propuse
14.5.1.
Se d o matrice A=(aij), i=1,...,n; j=1,...,m cu elementele din mulimea
{0,1,2}. Dou elemente din A aij i akl se numesc 4-vecine dac i-k+j-l =
1.
Notm cu So, S1 i S2 submulimile formate din elementele matricii egale
cu 0, 1 respectiv 2. Submulimea S1 se mparte n grupuri astfel: aij i akl
fac parte din acelai grup dac sunt 4-vecine sau dac apq S1 : aij i apq
sunt 4-vecine iar apk i akl sunt din acelasi grup. Un element akl So l vom
numi spion al grupului G S1 dac aij G a.. akl i aij s fie vecine. Un
spion este perfect dac are toi vecinii din G.
Se cere:
a) cel mai numeros grup (S1) care are un singur spion (dac exist).
b) toate grupurile care au cel puin doi spioni perfeci.
14.5.2.
Se d o matrice cu elemente care au valori din {d,o}, care reprezint
un teren cu drumuri {d} i obstacole {o}. n acest teren se afl un tanc i o
int. Acest tanc poate primi comenzi pentru rotirea evii (cu 90o n ambele
sensuri), pentru deplasare (n direcia evii cu o linie sau cu o coloan)
sau pentru tragere (n direcia evii pentru a nimeri inta) n cazul n care
ntre tanc i int nu exist nici un obstacol. Considernd c deplasarea nu
se poate efectua prin obstacole, se cere cel mai scurt drum necesar tancului
pentru a putea distruge inta i irul comenzilor efectuate n cazul n care
exist un astfel de drum.
14.5.3.
Se d o matrice cu elemente din {0,1,2,3}, reprezentnd o pdure cu
capcane (0) i crri (1). n aceast pdure se afl o vulpe (2) i mai muli
lupi (3). Fiecare lup ncearc s se apropie de vulpe fr a ti unde se afl
capcanele, iar n cazul n care cade ntr-o capcan, moare. Vulpea ncearc
s se ndeprteze de cel mai apropiat lup, avnd ns posibilitatea s
descopere i capcanele i s le ocoleasc. Att vulpea ct i lupii i pot
modifica poziia doar cu o linie sau cu o coloan n fiecare moment. S se
spun dac vulpea reuete s scape de lupi.
14.5.4.
Se consider un teren dreptunghiular sub forma unei matrici A de m
linii i n coloane. Elementele aij ale matricii conin cotele (nlimile)
diferitelor poriuni astfel obinute. Se presupune c o bil se gsete pe o
poriune (io,jo) avnd cota a(io,jo).
Se cere un program care s precizeze toate traseele (ncepnd cu (io,jo)
) posibile pe care le poate urma bila spre a iei din teren, tiind c bila
se poate deplasa n orice poriune de teren 4-nvecinat cu o cot strict
inferioar cotei terenului pe care se gsete bila.
14.5.5.
Se cere un program care rezolv problema labirintului (nerecursiv).
O matrice de m linii i n coloane reprezint un labirint dac:
a(i,j) = o - semnificnd culoar;

a(i,j) = 1 - semnificnd zid.


Un traseu de ieire pornind de la o poriune (io,jo) trebuie s fie o
succesiune de perechi (io, jo), (i1, j1) . . . (ik, jk) astfel nct 2 perechi
nvecinate s fie 4-vecine i a(ip,jp)=0
p=0, . . . ,k.

ANEX
UN MEMENTO AL SINTAXEI LIMBAJULUI C
SINTAXA LIMBAJULUI C
a)
b)
c)
d)
e)

n descrierea sintaxei limbajului vom folosi urmtoarele notaii:


::= cu semnificaia "prin definiie este";
pentru a separa alternativele unei definiii sintactice;
< > pentru definirea unor metavariabile;
[ ] pentru delimitarea elementelor opionale;
.. pentru a indica elementele care se pot repeta.

Exemplu: Pentru a defini un "ntreg_zecimal" ca o succesiune de cifre


semnificative vom scrie:
<ntreg_zecimal> ::= <Cifr_nenul> [<Cifr>] ..
A. Atomi lexicali
<Cuvinte_cheie>
::=
autobreakcasecharconstcontinuedefaultdodouble|
elseenumexternfloatforgotoifint|longregister
|
returnshortsignedsizeofstatic|struct
switchtypedef
unionunsignedvoidvolatilewhile
<Nume> ::= <Liter>[<Liter><Cifr>] ..
<Liter> ::= abcdefghijklmnopqrstuvwxyz
BCDEFGHIJKLMNOPQRS TUVWXYZ_
<Cifr> ::= 0123456789
<Constant> ::= <Constant_ntreag><Constant_real>
<Constant_enumerare><Constant_caracter>
<Constant_ntreag> ::= <Numr_ntreg>[<Sufix_ntreg>
<Numr_ntreg> ::= <ntreg_zecimal><ntreg_hexazecimal><ntreg_octal>
<ntreg_zecimal> ::= <Cifr_nenul>[<Cifr>] ..
<Cifr_nenul> ::= 123456789
<ntreg_hexazecimal> ::= 0x<Cifr_hexa> ..
<Cifr_hexa> ::= <Cifr>abcdefABCDEF
<ntreg_octal> ::= 0[<Cifr_octal>] ..
<Cifr_octal_> ::= 01234567

<Sufix_ntreg>
<Sufix_unsigned>[<Sufix_long>]<Sufix_long>[<Sufix_unsigned>]

::=

<Sufix_unsigned> ::= Uu
<Sufix_long> ::= Ll
<Constant_real> ::= <Constant_fract>[<Exponent>][<Sufix_real>]
<Cifr> .. <Exponent>[<Sufix_real>]
<Constant_fract> ::= [<Cifr>] .. . <Cifr>..<Cifr>.. .
<Exponent> ::= Ee[+-]<Cifr> ..
<Sufix_real> ::= FfLl
<Constant_enumerare> ::= <Nume>
<Constant_caracter> ::= '<Caracter_C> ..'L'<Caracter_C>'
<Caracter_C> ::= <Orice_caracter_tipribil_cu excepia: ' i \>
<Secven_escape>
<Secven_escape> ::= \"\'\?\\\a\b\f\n\r\t\v
\<Cifr_octal>[<Cifr_octal>[<Cifr_octal>]]

\x<Cifr_hexa>

..
<ir_de_caractere> ::= "[<Caracter_S> ..]"
<Caracter_S>
::=
\><Secvena_escape>

<Orice_caracter_tipribil_cu

<Operatori_i_semne_de_punctuaie >::=

excepia:

'

+-*/%^&~!=->+=-

=*=
/=%=^=&=!=<<>><<=>>== =<=
>=<>&&!!++-,()[]{};?:...
B. Declaraii
<Unitate_de_compilare> ::= <O_declaraie> ..
<O_declaraie> ::= <Def_funcie><Declaraie>;
<Declaraie> ::= [<Specificatori>][<List_declaratori>]
<Specificatori> ::= <Specificator> ..
<Specificator> ::= <Clas_memorare><Tip>typedef
<Clas_memorare> ::= autoregisterstaticextern
<Tip> ::= <Tip_simplu><Nume_typedef><Calificator>
<Descriere_tip_enumerare><Descriere_tip_neomogen>
<Tip_simplu> ::= charshortintlongsignedunsignedfloatdouble void
<Nume_typedef> ::= <Nume>

<Descriere_tip_enumerare> ::= enum<Nume>enum[<Nume>]{<Lista_enum>}


<Lista_enum> ::= <Enumerator>[,<Enumerator>] ..
<Enumerator> ::= <Nume>[=<Expr_constant_>{]
<Descriere_tip_neomogen> ::= <Tip_neomogen><Nume>
<Tip_neomogen>[<Nume>]{<List__cmpuri>}
<Tip_neomogen> ::= structunion
<List_cmpuri> ::=

<Decl_cmpuri>;[<Decl_cmpuri>;] ..

<Decl_cmpuri> ::= [<Specificatori>][<Cmp>[,<Cmp>] ..]


<Cmp> ::= <Declarator>[<Nume>]:<Expr_constant>
<List_declaratori> ::= <Decl_init>[,<Decl_init>] ..
<Decl_init> ::= <Declarator>[<Iniializare>]
<Declarator> ::= [<Indirect>..]<Denumire>[<Decl_F_T>]
<Indirect> ::=

*[<Calificator> ..]

<Denumire> ::= <Nume>(<Declarator>)


<Decl_F_T> ::= ([<L_declar_par>])<Decl_tablou> ..
<Decl_tablou> ::= [][<Expr_constant>]
<L_declar_par> ::= <Decl_par>[,<Decl_par>] .. [, ..]
<Decl_par> ::= <Tip>..<Declarator><Decl_tip>
<Decl_tip> ::= <Tip>..[<Indirect>..][(<Decl_tip>)][<Decl_F_T>]
<Iniializare> ::= =<Expr> ={<List_init>[,]}
<List_init>::=<Expr>[,<List_init> ..{<List_init>}[,<List_init>] ..
<Def_funcie>
..<Bloc>

::=

<Specif_funcie> ::=

[<Specif_funcie>

..]<Declarator>[<Declaraie>;]

externstatic<Tip>

C. Expresii
<Expr> ::= <Expresie>[,<Expresie>] ..
<Expresie>
<Expr_condiional>|<Expr_unar><Oper_atribuire><Expresie>
<Oper_atribuire> ::= +*=/=%=+=-=<<=>>=&=^=!=
<Expr_condiional> ::= <Expr_SAU_logic>
<Expr_SAU_logic> ? <Expr> : <Expr_condiional>

::=

<Expr_SAU_logic>
<Expr_I_logic>

::=

<Expr_I_logic> ::=

<Expr_I_logic><Expr_SAU_logic>

!!

<Expr_SAU><Expr_I_logic> && <Expr_SAU>

<Expr_SAU> ::= <Expr_SAU_exclusiv><Expr_SAU><Expr_SAU_exclusiv>


<Expr_SAU_exclusiv> ::= <Expr_I><Expr_SAU_exclusiv> ^ <Expr_I>
<Expr_I> ::= <Expr_egalitate><Expr_I> & <Expr_egalitate>
<Expr_egalitate>
<Expr_relaional><Expr_egalitate>==Expr_relaional>
<Expr_egalitate> != <Expr_relaional>
<Expr_relaional>
<Expr_deplasare>

::=

<Expr_deplasare>

<Expr_relaional>

::=

<

<Expr_relaional>

>

<Expr_relaional>

<=

<Expr_relaional>

>=

<Expr_deplasare>
<Expr_deplasare>
<Expr_deplasare>
<Expr_deplasare>
<Expr_aditiv>

::=

<Expr_aditiv>

<Expr_deplasare>

<<

<Expr_deplasare> >> <Expr_aditiv>


<Expr_aditiv>
<Expr_multiplicativ>

::=

<Expr_multiplicativ><Expr_aditiv>

<Expr_aditiv>

<Expr_multiplicativ>

<Expr_multiplicativ>

<Expr_multiplicativ>::=
<Expr_prefixat>

<Expr_multiplicativ> / <Expr_prefixat>
<Expr_multiplicativ> % <Expr_prefixat>
<Expr_prefixat> ::= <Expr_unar>(<Decl_tip>)<Expr_prefixat>
<Expr_unar> ::= <Expr_postfixat><Op_unar><Expr_prefixat>
++<Expr_unar> --<Expr_unar>sizeof<Expr_unar>
sizeof(<Decl_tip>)
<Op_unar> ::= &*+-~!
<Expr_postfixat> ::= <Termen><Expr_postfixat>[<Expr>]
<Expr_postfixat>(<List_Expresii>) <Expr_postfixat> .
<Nume>
<Expr_postfixat>
->
<Nume><Expr_postfixat>
++
<Expr_postfixat>-<Termen> ::= <Nume><Constant><ir_de_caractere>(<Expr>)
<List_Expresii> ::= [<Expr>] ..
<Expr_constant> ::= <Expr_condiional>

D. Instruciuni
<Instr>::=
<Instr_etichetat><Instr_compus>
<Instr_expresie>Instr_de_selecie>
<Instr_de_ciclare><Instr_de_salt>
<Instr_etichetat> ::= <Nume>:<Instr>case <Expr_constant { :<Instr>
default : <Instr>
<Instr_compus> ::= <Bloc>
<Bloc> ::= {[<Declaraie>;] .. [<Instr>] ..}
<Instr_expresie> ::= [<Expr>];

<Instr_de_selecie> ::= if (<Expr>) <Instr>if (<Expr>) <Instr> else


<Instr>
switch (<Expr>) <Instr>
<Instr_de_ciclare> ::=

while (<Expr>)<Instr>;
do <Instr> while (<Expr>);
for ( [<Expr>];[<Expr>];[<Expr>] ) [<Instr>];

<Instr_de_salt> ::= goto <Nume>;continue;break;return [<Expr>];

CUPRINS

BIBLIOGRAFIE
Brian W.Kerninghan, Dennis M. Ritchie, The C Programming Language, INC.
Englewood Cliffs, New Jersey, 1978.
Liviu Negrescu, Limbajul C, Editura Libris, Cluj-Napoca, 1997.
G. Moldovan, M. Lupea, V.Cioban, Probleme pentru programare n limbajul
C, Litografia Universitii Babe-Bolyai, Cluj-Napoca, 1995.

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