Documente Academic
Documente Profesional
Documente Cultură
- 1 -
radacina
patrata
calculeaza
din
radacina
si
de
de variabile locale i
jos.
limbaj de programare se
timpuriu vom da un mic
ntregii
ntregii
ntregii
ntregii
din
din
din
din
[-215; 215-1]
[-215; 215-1]
[-231; 231 - 1]
[0; 216-1]
[-3.4*10-38; 3.4*1038]
[-1.7*10-308; 1.7*10308]
16
16
32
16
8
32
64
reprezentare binar
reprezentare binar
reprezentare binar
reprezentare binar
cod ASCII
virgul flot. simpl prec.
virgul
flotant
dubl
65
66
97
48
58
77
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 printro 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 C trebuie declarate toate variabilele nainte de a fi
utilizate.
Exemple:
int a,b,c; // a, b, c sunt variabile de tip int reprezentate in binar pe
2 octei;
float x,y,z;
// x, y, z sunt variabile de tip float in format flotant
simpla precizie pe 32 de bii;
char car; // car este o variabila de tip char pe 1 octeti poate conine
coduri ASCII;
long i,j,k;
// 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];
//
s-a
declarat
un
tablou
unidimensional
de
5
componente;
float a[5][3];
// 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].
- 5 -
constante
BORLAND C
simbolice
care
macrouri.
- 7 -
- 8 -
LECIA 2.
V A R I A B I L E
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.
- 9 -
x,
locala
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.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};
- 11 -
cel
al
//
tabloul
de
tip
intreg
are
elemente
- 12 -
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) *(unar) &(unar) ! ~ ++ -- (tip) sizeof
* / %
+ << >>
< <= >= >
= = !=
&
^
|
&&
| |
? : (ternar)
= op= op poate fi: *(binar) / % +(binar) (binar) << >> & ^ |
,
- 13 -
~0
~0<<3
~(~0<<3)
- 16 -
unei
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.
- 17 -
date
se
poate
folosi
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
are
are
are
are
valoarea 2;
valoarea 4;
valoarea 4;
valoarea 8;
valoarea 1;
// are valoarea
// are valoarea
// are valoarea
// are valoarea
2;
8;
10;
40;
int
a spre double
b spre double
double
nu
double
a spre double
i spre double
i spre double
double
nu
int
i spre double
double
c spre int
int
10 spre double
double
10 spre unsigned
unsigned
5 spre long
long
se realizeaz mprirea ntreag ntre
i i j i rezultatul se convertete spre double double
- 18 -
- 19 -
- 20 -
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.
litera
tipul de conversie realizat
d
- din int intern n zecimal extern
o
- din int intern n octal extern
x
- din int intern n hexazecimal extern (litere mici pentru
cifrele mai mari ca 9 deci a,b,c,d,e,f,)
X
- din int intern n hexazecimal extern (litere mici pentru
cifrele mai mari ca 9 deci A,B,C,D,E,F)
u
- din unsigned intern n zecimal extern fr semn
c
- din binar intern (cod ASCII) n caracterul 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 = 00110
i*j = 110
a*b = 1.6
i*j = 110
A*B = 2.10000
i*j = 00110
radical(a*b) = 1.249000
- 22 -
radical(A*B) = 1.4491376746
radical(A*B) = 1.44913767461894372
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 *
radical(A*B) = 1.4491376746189437200
- 24 -
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);
ca litera mica }
- 26 -
LECIA 5.
I N S T R U C I U N I
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.
- 27 -
- 28 -
deci:
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;
}
- 30 -
f=1
i=2
CtTimp
Programul n C este:
#include<stdio.h>
void main (void)
execut
{ int n,i;
f=f*i;
double f;
i=i+1
f=1.0;
i=2;
SfritCtTimp
printf(\n dati n= );
Scrie n,f
scanf(%d,&n);
while (i<=n)
{ f=f*i;
i++;
}
printf(\nn=%d,
n!=%g\n,n,f);
}
Corpul ciclului while se poate scrie mai compact astfel:
while (i<=n) f*=i++;
i<=n
- 31 -
iar
while
(exp)
instructiune
este
- 32 -
Efectul:
se evalueaz expresia din parantez;
se compar pe rnd valoarea expresiei cu valorile constantelor c 1, . . . ,
cn;
while
if
si
}
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.
- 35 -
care
- 38 -
LECIA 6.
P O I N T E R I
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 reprezint tip dintr-o declaraie obinuit.
De aceea, construcia
tip *
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)++;
- 39 -
- 40 -
Exemplu:
tablou
6.3.2.
Dac p
p+n i
unde n
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
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++; // sau for (i=0; *x++; i++);
- 41 -
return 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 la alocare, deci este pointerul
spre nceputul zonei care se elibereaz.
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
- 42 -
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)();
- 43 -
- 44 -
parametrilor
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() // scoate elementul din varful stivei si returneaza valoarea lui
{ if (next >0)
- 46 -
else
}
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=";
- 47 -
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 determin recursiv suma unor elemente de tablou unidimensional
a[1]+a[2]+ . . . + a[n]
#include <iostream.h>
#define MAX 100
int a[MAX];
// suma(n)= 0,
daca n=0
// suma(n)=suma(n-1) + a[n]
daca n>0
int suma(int n)
{ if (!n)
return 0;
else
return a[n]+suma(n-1);
}
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 termenul al n-lea din irul lui Fibonacci
definit dup cum urmeaz:
fibonacci[0]=0
fibonacci[1]=1
fibonacci[n]=fibonacci[n-1]+fibonacci[n-2],
dac n>1
#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] }
#include<iostream.h>
#define MAX(x,y) (x > y ? x : y)
int a[100];
dac n>1
int M(int n)
{ if (n= =1)
return a[1];
else
return MAX (M(n-1), a[n]);
}
- 48 -
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) Programul sorteaz prin metoda quicksort un vector de numere ntregi:
- 49 -
#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];
}
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();
}
- 50 -
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.
- 51 -
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,
datei
dc
introdus
vom
in
folosi
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.
- 53 -
REAL x, y, z;
typdef struct
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
- 55 -
urmtoare
cnd
se
vor
folosi
//
se
foloseste
// se foloseste us.uu.f
// se foloseste us.uu.d
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
- 58 -
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
- 59 -
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.
Observaii:
1o. Se pot utiliza, ca i n cazul structurilor, construcii de forma:
enum nume {nume0, nume1,. . . , numen} lista_de_variabile;
sau
enum { nume0, nume1,. . . , numen} lista_de_variabile;
2o. De asemenea, se poate utiliza construcia typedef pentru a atribui un
nume unui tip enumerat:
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
Prin prima declaraie se introduce tipul enumerat luna. Mulimea de
valori asociate acestui tip este format din numerele ntregi 1,2, . . . , 12.
Se pot utiliza denumirile:
ian ia valoarea 1
feb ia valoarea 2
. . .
dec ia 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
- 61 -
LECIA 9.
L I S T E
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;
poate
// declara tipul t2
struct t2
{ declaratii
struct t1 *pt1;
declaratii
};
struct t1
{ declaratii
struct t2 *pt2;
declaratii
};
9.2. LISTE NLNUITE
- 62 -
- 63 -
// functia principala
// lista e cunoscuta prin adresa capului
- 64 -
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;
- 65 -
- O_APPEND
- O_BINARY
- O_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
directorul curent.
citire
fiierul
fis1.dat
din
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.
- 66 -
- 67 -
#include<io.h>
#include<fcntl.h>
void main (void)
{ int df,i;
df = open("fis1.dat", O_CREAT);// se deschide fisierul fis1.dat in creare
if (df != -1)
// se testeaza daca fiserul s-a deschis corect
{ write (df,"cioban vasyle\n", 14);// se scriu doua inregistrari
write (df,"cioban andrei\n", 14);
} else printf (nu s-a deschis fisierul);
close (df);
// 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 nivel fiierele se prelucreaz cu ajutorul
unor proceduri specializate.
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 i 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.
- 68 -
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 scrise i citite caracter cu caracter, folosind dou
funcii simple:
putc pentru scriere;
getc pentru citire.
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 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 standard la ieirea standard stdout,
folosind funciile getc i putc.
#include <stdio.h>
void main (void)
{ int c;
while (( c = getc (stdin)) != EOF) putc (c, stdout);
}
Programul urmtor copiaz intrarea standard la imprimant.
#include <stdio.h>
void main (void)
{ int c;
while (( c = getc (stdin)) != EOF) putc (c, stdprn);
}
- 69 -
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.
- 71 -
}
str -> pret = x;
str -> cant = y;
return (nr);
}
// sfarsit while
// sfarsit cit
- 73 -
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 de la punctele a) i b) au fost descrise n lecii anterioare.
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 i l rescrie schimbnd literele mari
cu litere mici. Cile spre cele dou fiiere (surs i destinaie) sunt
argumente n linia de comand:
argv[1]
este un pointer spre fiierul surs;
argv[2]
este un pointer spre 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);
}
- 74 -
fiierul ctype.h.
ai
lui
(care
n liter mic;
n 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);
- 75 -
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;
- 76 -
-arccos;
-arcsin;
-arctg;
-cos;
-sin;
-ex;
-ln;
-lg;
-rdcina ptrat;
ceil
Alte funcii:
double atan2 (double y, double x); - returneaz arctg(y/x);
double pow (double x, double y);
- returneaz xy;
double cabs (struct complex z);
- returneaz modulul nr. 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); - returneaz valoarea absolut din ntregul long n;
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 char *comanda)
- execut o comand DOS definit
prin sirul de caractere spre care pointeaz comanda; returneaz 0 la succes si
1 la eroare. Aceste funcii au prototipurile n stdlib.h i n process.h.
- 77 -
LECIA 12.
- 78 -
ecranul;
perioad
perioad
ton egal
Valoare
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
128
Constant simbolic
BW40
C40
BW80
C80
MONO
Valoare
0
1
2
3
7
C4350
LASTMODE
64
-1
funciei
textcolor
cu
#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 de culori posibile pentru
fond i caractere (adaptor EGA/VGA).
#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);
textattr (atribut. attribute);
clrscr();
}
- 83 -
LECIA 13.
- 84 -
simbol
culori
CGAHI
are o rezoluie de 640*200 puncte i lucreaz numai alb/negru.
pentru EGA
valoare
simbol
simbol
VGALO are 640*200
VGAMED
are 640*350
VGAHI are 640*480
constanta
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:
indexul
tabloul
care
definete
paleta
folosind
pentru
funcia
modific
culoarea
folosind
funcia
dimensiunea
paletei
este
funcia
paletei. n
cazul
Exemplu:
Programul urmtor afieaz codurile culorilor pentru paleta implicit:
#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++)
{ printf (colors[%d]=%d\n, i, pal -> colors[i]);
getch ();
}
closegraph();
int
pixelului
int
pixelului
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();
}
- 89 -
- 90 -
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 m-combinare. 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 m-combinrile.
#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);
- 91 -
}
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.
- 92 -
Problem rezolvat
Se d o mulime de valori reale X={x1, . . .,xn}. Se cere submulimea YX
astfel ca y /yY, s fie maxim.
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;
- 93 -
primul(n)
reprezint
vectorul
primilor
termeni
ai
progresiilor
else x[++k]=0;
}
}
se
14.3.6.
Avnd un graf neorientat caracterizat prin matricea de inciden
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
- 97 -
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
final;
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;
- 98 -
- 99 -
{
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.
- 101 -
- 102 -
ANEX
UN MEMENTO AL SINTAXEI LIMBAJULUI C
SINTAXA LIMBAJULUI C
n descrierea sintaxei limbajului vom folosi urmtoarele notaii:
a) ::= cu semnificaia "prin definiie este";
b) pentru a separa alternativele unei definiii sintactice;
c) < > pentru definirea unor metavariabile;
d) [ ] pentru delimitarea elementelor opionale;
e) .. 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_long>[<Sufix_unsigned>]
::=
<Sufix_unsigned>[<Sufix_long>]
<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> ..
- 103 -
::=
<Orice_caracter_tipribil_cu
excepia:
'
\>
<Operatori_i_semne_de_punctuaie >::=
+-*/%^&~!=->+=-=*=
/=%=^=&=!=<<>><<=>>== =<=
>=<>&&!!++--,()[]{};?:...
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>;] ..
*[<Calificator> ..]
::=
[<Specif_funcie>
..]<Declarator>[<Declaraie>;]
..
<Bloc>
<Specif_funcie> ::=
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_SAU_logic> !! <Expr_I_logic>
<Expr_I_logic> ::=
<Expr_relaional>
<Expr_deplasare>
::=
<Expr_deplasare>
<Expr_relaional>
<
<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>
<Instr_expresie>
::=
if
(<Expr>)
<Instr>if
(<Expr>)
<Instr>
<Instr_de_ciclare> ::=
while (<Expr>)<Instr>;
do <Instr> while (<Expr>);
for ( [<Expr>];[<Expr>];[<Expr>] ) [<Instr>];
- 106 -
else
- 107 -
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.