Cuprins
1.
2.
1. Algoritmic i programare
1.1. Cutri i sortri
1.1.1.
Cutri
Datele se afl n memoria intern, ntr-un ir de articole. Vom cuta un articol dup un
cmp al acestuia pe care l vom considera cheie de cutare. n urma procesului de cutare va
rezulta poziia elementului cutat (dac acesta exist).
Notnd cu k1, k2, ...., kn cheile corespunztoare articolelor i cu a cheia pe care o cutm,
problema revine la a gsi (dac exist) poziia p cu proprietatea a = kp.
De obicei articolele sunt pstrate n ordinea cresctoare a cheilor, deci vom presupune c
k1 < k2 < .... < kn .
Uneori este util s aflm nu numai dac exist un articol cu cheia dorit ci i s gsim n caz
contrar locul n care ar trebui inserat un nou articol avnd cheia specificat, astfel nct s se
pstreze ordinea existent.
Deci problema cutrii are urmtoarea specificare:
Date a,n,(ki, i=1,n);
Precondiia: nN, n1 i k1 < k2 < .... < kn ;
Rezultate p;
Postcondiia: (p=1 i a k1) sau (p=n+1 i a > kn) sau (1<pn) i (kp-1 < a kp).
1.1.1.1.
Cutare secvenial
O prim metod este cutarea secvenial, n care sunt examinate succesiv toate cheile.
Sunt deosebite trei cazuri: ak1, a>kn, respectiv k1 < a kn, cutarea avnd loc n al treilea caz.
{nN, n1 i k1 < k2 < .... < kn}
{Se caut p astfel ca: (p=1 i a k1) sau}
{ (p=n+1 i a>kn) sau (1<pn) i (kp-1 < a kp).
{Cazul "nc negasit"}
Fie p := 0;
Dac a k1 atunci p := 1 altfel
Dac a > kn atunci p := n + 1 altfel
Pentru i := 2; n execut
Dac (p = 0) i (a ki) atunci p := i sfdac
sfpentru
sfdac
sfdac
sf-CautSecv
Se observ c prin aceast metod se vor executa n cel mai nefavorabil caz n-1 comparri,
ntruct contorul i va lua toate valorile de la 2 la n. Cele n chei mpart axa real n n+1 intervale.
Tot attea comparri se vor efectua n n-1 din cele n+1 intervale n care se poate afla cheia
cutat, deci complexitatea medie are acelai ordin de mrime ca i complexitatea n cel mai ru
caz.
Evident c n multe situaii acest algoritm face calcule inutile. Atunci cnd a fost deja
gsit cheia dorit este inutil a parcurge ciclul pentru celelalte valori ale lui i. Cu alte cuvinte
este posibil s nlocuim ciclul PENTRU cu un ciclu CTTIMP. Ajungem la un al doilea
algoritm, dat n continuare.
{nN, n1 i k1 < k2 < .... < kn}
{Se caut p astfel ca: p=1 i a k1) sau }
{(p=n+1 i a>kn) sau (1<pn) i (kp-1 < a kp).
Fie p:=1;
Dac a>k1 atunci
Cttimp pn i a>kp execut p:=p+1 sfct
sfdac
sf-CautSucc
n cel mai ru caz i acest algoritm face acelai numr de operaii ca i subalgoritmul
Cautsecv. n medie numrul operaiilor este jumtate din numrul mediu de operaii efecuat de
subalgoritmul Cautsecv deci complexitatea este aceeai.
1.1.1.2.
Cutare binar
O alt metod, numit cutare binar, care este mult mai eficient, utilizeaz tehnica
"divide et impera" privitor la date. Se determin n ce relaie se afl cheia articolului aflat n
mijlocul coleciei cu cheia de cutare. n urma acestei verificri cutarea se continu doar ntr-o
jumtate a coleciei. n acest mod, prin njumtiri succesive se micoreaz volumul coleciei
rmase pentru cutare. Cutarea binar se poate realiza practic prin apelul funciei
BinarySearch(a, n, K, 1, n), descris mai jos, folosit n subalgoritmul dat n continuare.
{nN, n1 i k1 < k2 < .... < kn}
{Se caut p astfel ca: (p=1 i a k1) sau}
{(p=n+1 i a>kn) sau (1<pn) i (kp-1 < a kp)}
1.1.2.
Sortri
Prin sortare intern vom nelege o rearanjare a unei colecii aflate n memoria intern
astfel nct cheile articolelor s fie ordonate cresctor (eventual descresctor).
Din punct de vedere al complexitii algoritmilor problema revine la ordonarea cheilor.
Deci specificarea problemei de sortare intern este urmtoarea:
Date n,K;
{K=(k1,k2,...,kn)}
1.1.2.1.
1.1.2.2.
Bubble sort
Metoda "BubbleSort", compar dou cte dou elemente consecutive iar n cazul n care
acestea nu se afl n relaia dorit, ele vor fi interschimbate. Procesul de comparare se va ncheia
n momentul n care toate perechile de elemente consecutive sunt n relaia de ordine dorit.
Subalgoritmul BubbleSort(n, K) este:
Repet
Fie kod := 0;
Pentru i := 2; n execut
Dac ki-1 > ki atunci
t := ki-1;
ki-1 := ki;
ki := t;
kod := 1
sfdac
sfpentru
pncnd kod = 0 sfrep
sf-BubbleSort
{Ordonare}
Acest algoritm execut n cel mai nefavorabil caz (n-1)+(n-2)+ ... +2+1 = n(n-1)/2
comparri, deci complexitatea lui este O(n2).
O variant optimizat a algoritmului "BubbleSort" este :
1.1.2.3.
{Ordonare}
Quicksort
Procedura QuickSort(n, K, St, Dr) va realiza ordonarea subirului kSt, kSt+1, ...,
kDr. Acest subir va fi rearanjat astfel nct kSt s ocupe poziia lui final (cnd irul este
ordonat). Dac i este aceast poziie, irul va fi rearanjat astfel nct urmtoarea condiie s fie
ndeplinit:
kj ki kl , pentru st j < i < l dr
(*)
Odat realizat acest lucru, n continuare va trebui doar s ordonm subirul kSt, kSt+1,
... ,ki-1 prin apelul recursiv al procedurii QuickSort(n, K, St, i-1) i apoi subirul ki+1,
...,kDr prin apelul QuickSort(n, K, i+1, Dr). Desigur ordonarea acestor dou subiruri
(prin apelul recursiv al procedurii) mai este necesar doar dac acestea conin cel puin dou
elemente.
Procedura QuickSort este prezentat n continuare :
Complexitatea algoritmului prezentat este O(n2) n cel mai nefavorabil caz, dar
complexitatea medie este de ordinul O(nlog2n).
1.2.1.
Metoda backtracking
x1
x2
x3 1
Start
x1
x1
...
...
x2
x3
...
...
... n
... n
...
... n
...
...
xk-1
...
xk
. . . xk
xk+1
...
xn
Cautare pe nivel
x2
...
. . . x3 . . . n
...
xk-1
... n
... n
...
... n
...
xk-1+1
xk+1
... n
... i ... n
Cautare cu succes:
1
pas in fata
...
n
Pentru a finisa acest algoritm trebuie s precizm elementele nestandard prezente. Astfel,
avem nevoie de funcia boolean
condiii-continuare(k, posibil, v)
funcie care verific dac vectorul promitor posibil[1..k-1] completat cu valoarea v conduce la
un vector promitor.
Apoi, pentru a iniializa cutarea la nivelul j avem nevoie de a alege un element fictiv din
mulimea Sj, activitate realizat de funcia
10
init(j)
care returneaz acest element fictiv, care are rolul de a indica faptul c din mulimea S nc nu sa ales nici un element, deci dup el urmeaz primul element propriu din aceast mulime. Pentru
a cuta o valoare pe nivelul j, n ipoteza c valoarea curent nu e bun, avem nevoie de funcia
boolean
urmtor(j, v, nou)
care este True dac poate alege o valoare din Sj care urmeaz dup valoarea v, valoare notat
prin nou i False n cazul n care nu mai exist alte valori n Sj, deci nu mai poate fi fcut
alegerea. Cu aceste notaii algoritmul devine:
Algoritmul Backtracking este:
{versiune final}
Fie k := 1;
posibil[1] := init(1);
Cttimp k > 0 execut
{posibil[1..k-1] este promitor}
Fie Gsit := false; v := posibil[k] ;
Cttimp Urmtor(k, v,urm) i not Gsit execut
Fie v := urm;
Dac condiii-continuare(k, posibil, v) atunci
Gsit := true
sfdac
sfct
Dac Gsit
atunci Fie posibil[k] := v;
{posibil[1..k] este promitor}
Dac soluie(n, k, posibil)
atunci
{o soluie! (rmnem pe nivelul k)}
Tiparete posibil[1..k]
altfel
{e doar un vector promitor}
Fie k := k + 1;
{pas n fa (pe nivelul k+1)}
posibil[k] := init(k)
sfdac
altfel
{pas n spate (revenire pe nivelul k-1)}
k := k - 1;
sfdac
sfct
sf-Backtracking
11
sfct
conditii-continuare:=kod
sf-conditii
Funcia soluii(n, k, posibil) este:
Soluii := (k = n)
sf-solutii
este:
1.2.2.
12
mparte: Dac dimensiunea datelor este prea mare pentru a fi rezolvabil imediat,
mparte problema n una sau mai multe subprobleme independente (similare
problemei iniiale).
1.3.1.
1.3.1.1.
Noiunea de clas
//elementele vectorului
//dimensiunea vectorului
void lapatrat()
//ridicare la patrat
{
for(int i = 0; i < d; i++)
e[i] *= e[i];
}
void afiseaza()
//afisare
{
for(int i = 0; i < d; i++)
cout << e[i] << ' ';
cout << endl;
}
14
Observm c dei n programul principal se lucreaz cu doi vectori nu putem s-i folosim
mpreun, deci de exemplu modulul vector1.cpp nu poate fi extins astfel nct s realizeze i
adunarea a doi vectori. n vederea nlturrii acestui neajuns s-au introdus tipurile abstracte
de date.
1.3.1.2.
Tipurile abstracte de date realizeaz o legtur mai strns ntre datele problemei i operaiile
(funciile) care se refer la aceste date. Declararea unui tip abstract de date este asemntoare
cu declararea unei structuri, care n afar de date mai cuprinde i declararea sau definirea
funciilor referitoare la acestea.
De exemplu n cazul vectorilor cu elemente numere ntregi putem declara tipul abstract:
struct vect {
int* e;
int d;
void init(int* e1, int d1);
void distr() { delete [] e; }
void lapatrat();
void afiseaza();
};
Funciile declarate sau definite n interiorul structurii vor fi numite funcii membru iar datele
date membru. Dac o funcie membru este definit n interiorul structurii (ca i funcia distr
din exemplul de mai sus), atunci ea se consider funcie inline. Dac o funcie membru se
definete n afara structurii, atunci numele funciei se va nlocui cu numele tipului abstract
urmat de operatorul de rezoluie (::) i numele funciei membru. Astfel funciile init, lapatrat
i afiseaza vor fi definite n modul urmtor:
void vect::init(int *e1, int d1)
{
d = d1;
e = new int[d];
for(int i = 0; i < d; i++)
e[i] = e1[i];
}
void vect::lapatrat()
{
for(int i = 0; i < d; i++)
e[i] *= e[i];
}
void vect::afiseaza()
{
for(int i = 0; i < d; i++)
cout << e[i] << ' ';
cout << endl;
}
15
Dei prin metoda de mai sus s-a realizat o legtur ntre datele problemei i funciile
referitoare la aceste date, ele nu sunt protejate, deci pot fi accesate de orice funcie utilizator,
nu numai de funciile membru. Acest neajuns se poate nltura cu ajutorul claselor.
1.3.1.3.
Declararea claselor
Un tip abstract de date clas se declar ca i o structur, dar cuvntul cheie struct se
nlocuiete cu class. Ca i n cazul structurilor referirea la tipul de dat clas se face cu
numele dup cuvntul cheie class (numele clasei). Protecia datelor se realizeaz cu
modificatorii de protecie: private, protected i public. Dup modificatorul de protecie se
pune caracterul :. Modificatorul private i protected reprezint date protejate, iar public date
neprotejate. Domeniul de valabilitate a modificatorilor de protecie este pn la urmtorul
modificator din interiorul clasei, modificatorul implicit fiind private. Menionm c i n
cazul structurilor putem s folosim modificatori de protecie, dar n acest caz modificatorul
implicit este public.
De exemplu clasa vector se poate declara n modul urmtor:
class vector {
int* e; //elementele vectorului
int d;
//dimensiunea vectorului
public:
vector(int* e1, int d1);
~vector() { delete [] e; }
void lapatrat();
void afiseaza();
};
Se observ c datele membru e i d au fost declarate ca date de tip private (protejate), iar
funciile membru au fost declarate publice (neprotejate). Bineneles, o parte din datele
membru pot fi declarate publice, i unele funcii membru pot fi declarate protejate, dac
natura problemei cere acest lucru. n general, datele membru protejate pot fi accesate numai
de funciile membru ale clasei respective i eventual de alte funcii numite funcii prietene
(sau funcii friend).
O alt observaie important referitoare la exemplul de mai sus este c iniializarea datelor
membru i eliberarea zonei de memorie ocupat s-a fcut prin funcii membru specifice.
Datele declarate cu ajutorul tipului de dat clas se numesc obiectele clasei, sau simplu
obiecte. Ele se declar n mod obinuit n forma:
nume_clas list_de_obiecte;
16
este un destructor.
Tipurile abstracte de date de tip struct pot fi i ele considerate clase cu toate elementele
neprotejate. Constructorul de mai sus este declarat n interiorul clasei, dar nu este definit, iar
destructorul este definit n interiorul clasei. Rezult c destructorul este o funcie inline.
Definirea funciilor membru care sunt declarate, dar nu sunt definite n interiorul clasei se
face ca i n cazul tipurilor abstracte de date de tip struct, folosind operatorul de rezoluie.
1.3.1.4.
Referirea la datele respectiv funciile membru ale claselor se face cu ajutorul operatorilor
punct (.) sau sgeat (->) ca i n cazul referirii la elementele unei structuri. De exemplu, dac
se declar:
vector v;
vector* p;
n interiorul funciilor membru ns referirea la datele respectiv funciile membru ale clasei se
face simplu prin numele acestora fr a fi nevoie de operatorul punct (.) sau sgeat (->). De
fapt compilatorul genereaz automat un pointer special, pointerul this, la fiecare apel de
funcie membru, i folosete acest pointer pentru identificarea datelor i funciilor membru.
Pointerul this va fi declarat automat ca pointer ctre obiectul curent. n cazul exemplului de
mai sus pointerul this este adresa vectorului v respectiv adresa referit de pointerul p.
Dac n interiorul corpului funciei membru afiseaza se utilizeaz de exemplu data membru d,
atunci ea este interpretat de ctre compilator ca i this->d.
Pointerul this poate fi folosit i n mod explicit de ctre programator, dac natura problemei
necesit acest lucru.
1.3.1.5.
Constructorul
tipul parametrilor formali trebuie s fie diferit, altfel compilatorul nu poate s aleag
constructorul corespunztor.
Constructorul nu returneaz o valoare. n acest caz nu este permis nici folosirea cuvntului
cheie void.
Prezentm n continuare un exemplu de tip clasa cu mai muli constructori, avnd ca date
membru numele i prenumele unei persoane, i cu o funcie membru pentru afiarea numelui
complet.
Fiierul persoana.h:
class persoana {
char* nume;
char* prenume;
public:
persoana();
persoana(char* n, char* p);
persoana(const persoana& p1);
~persoana();
void afiseaza();
};
//constructor implicit
//constructor
//constructor de copiere
//destructor
Fiierul persoana.cpp:
#include <iostream>
#include <cstring>
#include "persoana.h"
using namespace std;
persoana::persoana()
{
nume = new char[1];
*nume = 0;
prenume = new char[1];
*prenume = 0;
cout << "Apelarea constructorului implicit." << endl;
}
persoana::persoana(char* n, char* p)
{
nume = new char[strlen(n)+1];
prenume = new char[strlen(p)+1];
strcpy(nume, n);
strcpy(prenume, p);
cout << "Apelare constructor (nume, prenume).\n";
}
persoana::persoana(const persoana& p1)
{
nume = new char[strlen(p1.nume)+1];
strcpy(nume, p1.nume);
prenume = new char[strlen(p1.prenume)+1];
strcpy(prenume, p1.prenume);
cout << "Apelarea constructorului de copiere." << endl;
}
18
persoana::~persoana()
{
delete[] nume;
delete[] prenume;
}
void persoana::afiseaza()
{
cout << prenume << ' ' << nume << endl;
}
Fiierul persoanaTest.cpp:
#include "persoana.h"
int main() {
persoana A;
//se apeleaza constructorul implicit
A.afiseaza();
persoana B("Stroustrup", "Bjarne");
B.afiseaza();
persoana *C = new persoana("Kernighan","Brian");
C->afiseaza();
delete C;
persoana D(B);
//echivalent cu persoana D = B;
//se apeleaza constructorul de copire
D.afiseaza();
return 0;
}
19
20
EF.afiseaza();
return 0;
}
Observm c n cazul celui de al doilea constructor, parametrii formali sot i sotie au fost
declarai ca i referine la tipul persoana. Dac ar fi fost declarai ca parametri formali de tip
persoana, atunci n cazul declaraiei:
pereche AB(A, B);
constructorul de copiere s-ar fi apelat de patru ori. n astfel de situaii se creeaz mai nti
obiecte temporale folosind constructorul de copiere (dou apeluri n cazul de fa), dup care
se execut constructorii datelor membru de tip obiect (nc dou apeluri).
1.3.1.6.
Destructorul
Destructorul este funcia membru care se apeleaz n cazul distrugerii obiectului. Destructorul
obiectelor globale se apeleaz automat la sfritul funciei main ca parte a funciei exit. Deci,
nu este indicat folosirea funciei exit ntr-un destructor, pentru c acest lucru duce la un ciclu
infinit. Destructorul obiectelor locale se execut automat la terminarea blocului n care s-au
definit. n cazul obiectelor alocate dinamic, de obicei destructorul se apeleaz indirect prin
operatorul delete (obiectul trebuie s fi fost creat cu operatorul new). Exist i un mod
explicit de apelare a destructorului, n acest caz numele destructorului trebuie precedat de
numele clasei i operatorul de rezoluie.
Numele destructorului ncepe cu caracterul ~ dup care urmeaz numele clasei. Ca i n cazul
constructorului, destructorul nu returneaz o valoare i nu este permis nici folosirea
cuvntului cheie void. Apelarea destructorului n diferite situaii este ilustrat de urmtorul
exemplu. Fiierul destruct.cpp:
#include <iostream>
#include <cstring>
using namespace std;
class scrie { //scrie pe stdout ce face.
char* nume;
public:
scrie(char* n);
~scrie();
};
scrie::scrie(char* n)
{
nume = new char[strlen(n)+1];
strcpy(nume, n);
cout << "Am creat obiectul: " << nume << '\n';
}
scrie::~scrie()
{
cout << "Am distrus obiectul: " << nume << '\n';
delete nume;
21
}
void functie()
{
cout << "Apelare functie" << '\n';
scrie local("Local");
}
scrie global("Global");
int main() {
scrie* dinamic = new scrie("Dinamic");
functie();
cout << "Se continua programul principal" << '\n';
delete dinamic;
return 0;
}
Bazele teoretice
Prin folosirea tipurilor abstracte de date, se creeaz un tot unitar pentru gestionarea datelor i
a operaiilor referitoare la aceste date. Cu ajutorul tipului abstract clas se realizeaz i
protecia datelor, deci n general elementele protejate nu pot fi accesate dect de funciile
membru ale clasei respective. Aceast proprietate a obiectelor se numete ncapsulare
(encapsulation).
n viaa de zi cu zi ns ne ntlnim nu numai cu obiecte separate, dar i cu diferite legturi
ntre aceste obiecte, respectiv ntre clasele din care obiectele fac parte. Astfel se formeaz o
ierarhie de clase. Rezult a doua proprietate a obiectelor: motenirea (inheritance). Acest
lucru nseamn c se motenesc toate datele i funciile membru ale clasei de baz de ctre
clasa derivat, dar se pot aduga elemente noi (date membru i funcii membru) n clasa
derivat. n cazul n care o clas derivat are mai multe clase de baz se vorbete despre
motenire multipl.
O alt proprietate important a obiectelor care aparin clasei derivate este c funciile membru
motenite pot fi suprancrcate. Acest lucru nseamn c o operaie referitoare la obiectele
care aparin ierarhiei are un singur identificator, dar funciile care descriu aceast operaie pot
fi diferite. Deci, numele funciei i lista parametrilor formali este aceeai n clasa de baz i n
clasa derivat, dar descrierea funciilor difer ntre ele. Astfel, n clasa derivat funciile
membru pot fi specifice clasei respective, dei operaia se identific prin acelai nume.
Aceast proprietate se numete polimorfism.
1.4.2.
22
sau
protected clas_de_baz_i
sau
private clas_de_baz_i
Accesul la
elementele din clasa
de baz
Modificatorii de
protecie referitoare
la clasa de baz
Accesul la
elementele din clasa
derivat
public
public
public
protected
public
protected
private
public
inaccesibil
public
protected
protected
protected
protected
protected
private
protected
inaccesibil
public
private
private
protected
private
private
private
private
inaccesibil
23
1.4.3.
Funcii virtuale
24
ns acest lucru nu este rezultatul dorit, deoarece n cadrul funciei main s-a apelat funcia
membru functia_2 motenit de la clasa de baz, dar funcia membru functia_1 apelat de
functia_2 s-a determinat nc n faza de compilare. n consecin, dei funcia membru
functia_1 s-a suprancrcat n clasa derivat nu s-a apelat funcia suprancrcat ci funcia
membru a clasei de baz.
Acest neajuns se poate nltura cu ajutorul introducerii noiunii de funcie membru virtual.
Dac funcia membru este virtual, atunci la orice apelare a ei, determinarea funciei membru
corespunztoare a ierarhiei de clase nu se va face la compilare ci la execuie, n funcie de
natura obiectului pentru care s-a fcut apelarea. Aceast proprietate se numete legare
dinamic, iar dac determinarea funciei membru se face la compilare, atunci se vorbete de
legare static.
Am vzut c dac se execut programul virtual1.cpp se apeleaz funciile membru
functia_1 i functia_2 ale clasei de baz. ns funcia membru functia_1 fiind suprancrcat
n clasa derivat, ar fi de dorit ca funcia suprancrcat s fie apelat n loc de cea a clasei de
baz.
Acest lucru se poate realiza declarnd functia_1 ca funcie membru virtual. Astfel, pentru
orice apelare a funciei membru functia_1, determinarea acelui exemplar al funciei membru
din ierarhia de clase care se va executa, se va face la execuie i nu la compilare. Ca urmare,
funcia membru functia_1 se determin prin legare dinamic.
n limbajul C++ o funcie membru se declar virtual n cadrul declarrii clasei respective n
modul urmtor: antetul funciei membru se va ncepe cu cuvntul cheie virtual.
Dac o funcie membru se declar virtual n clasa de baz, atunci suprancrcrile ei se vor
considera virtuale n toate clasele derivate ale ierarhiei.
n cazul exemplului de mai sus declararea clasei de baz se modific n felul urmtor.
class baza {
public:
virtual void functia_1();
void functia_2();
};
#include <iostream>
using namespace std;
class fractie {
protected:
int numarator;
int numitor;
public:
fractie(int numarator1 = 0, int numitor1 = 1);
fractie produs(fractie& r);
//calculeaza produsul a doua
//fractii, dar nu simplifica
fractie& inmulteste(fractie& r);
void afiseaza();
};
fractie::fractie(int numarator1, int numitor1)
{
numarator = numarator1;
numitor
= numitor1;
}
fractie fractie::produs(fractie& r)
{
return fractie(numarator * r.numarator, numitor * r.numitor);
}
fractie& fractie::inmulteste(fractie& q)
{
*this = this->produs(q);
return *this;
}
void fractie::afiseaza()
{
if ( numitor )
cout << numarator << " / " << numitor;
else
cerr << "Fractie incorecta";
}
class fractie_scrie: public fractie{
public:
fractie_scrie( int numarator1 = 0, int numitor1 = 1 );
fractie produs( fractie& r);
};
inline fractie_scrie::fractie_scrie(int numarator1, int numitor1) :
fractie(numarator1, numitor1)
{
}
fractie fractie_scrie::produs(fractie& q)
{
fractie r = fractie(*this).produs(q);
cout << "(";
this->afiseaza();
cout << ") * (";
q.afiseaza();
cout << ") = ";
26
r.afiseaza();
cout << endl;
return r;
}
int main()
{
fractie p(3,4), q(5,2), r;
r = p.inmulteste(q);
p.afiseaza();
cout << endl;
r.afiseaza();
cout << endl;
fractie_scrie p1(3,4), q1(5,2);
fractie r1, r2;
r1 = p1.produs(q1);
r2 = p1.inmulteste(q1);
p1.afiseaza();
cout << endl;
r1.afiseaza();
cout << endl;
r2.afiseaza();
cout << endl;
return 0;
}
/
/
/
/
/
/
8
8
4) * (5 / 2) = 15 / 8
8
8
8
Observm c rezultatul nu este cel dorit, deoarece operaia de nmulire s-a afiat numai o
singur dat, i anume pentru expresia r1 = p1.produs(q1). n cazul expresiei r2 =
p1.inmulteste(q1) ns nu s-a afiat operaia de nmulire. Acest lucru se datoreaz
faptului c funcia membru inmulteste nu s-a suprancrcat n clasa derivat. Deci s-a apelat
funcia motenit de la clasa fractie. n interiorul funciei inmulteste s-a apelat funcia
membru produs, dar deoarece aceast funcie membru s-a determinat nc n faza de
compilare, rezult c s-a apelat funcia referitoare la clasa fractie i nu cea referitoare la clasa
derivat fractie_scrie. Deci, afiarea operaiei s-a efectuat numai o singur dat.
Soluia este, ca i n exemplul anterior, declararea unei funcii membru virtuale, i anume
funcia produs se va declara ca funcie virtual. Deci declararea clasei de baz se modific n
felul urmtor:
class fractie {
protected:
int numarator;
int numitor;
public:
fractie(int numarator1 = 0, int numitor1 = 1);
virtual fractie produs(fractie& r); //calculeaza produsul a doua
//fractii, dar nu simplifica
fractie& inmulteste(fractie& r);
27
void afiseaza();
};
/
/
/
/
/
/
/
8
8
4) * (5 / 2) = 15 / 8
4) * (5 / 2) = 15 / 8
8
8
8
Deci, se observ c afiarea operaiei s-a fcut de dou ori, pentru ambele expresii. Funciile
virtuale, ca i alte funcii membru de fapt, nu trebuie neaprat suprancrcate n clasele
derivate. Dac nu sunt suprancrcate atunci se motenete funcia membru de la un nivel
superior.
Determinarea funciilor membru virtuale corespunztoare se face pe baza unor tabele
construite i gestionate n mod automat. Obiectele claselor care au funcii membru virtuale
conin i un pointer ctre tabela construit. De aceea gestionarea funciilor membru virtuale
necesit mai mult memorie i un timp de execuie mai ndelungat.
1.4.4.
Clase abstracte
n cazul unei ierarhii de clase mai complicate, clasa de baz poate avea nite proprieti
generale despre care tim, dar nu le putem defini numai n clasele derivate. De exemplu s
considerm ierarhia de clase din figura 1.
Observm c putem determina nite proprieti referitoare la clasele derivate. De exemplu
greutatea medie, durata medie de via i viteza medie de deplasare. Aceste proprieti se vor
descrie cu ajutorul unor funcii membru. n principiu i pentru clasa animal exist o greutate
medie, durat medie de via i vitez medie de deplasare. Dar aceste proprieti ar fi mult
mai greu de determinat i ele nici nu sunt importante pentru noi ntr-o generalitate de acest
fel. Totui pentru o tratare general ar fi bine, dac cele trei funcii membru ar fi declarate n
clasa de baz i definite n clasele derivate. n acest scop s-a introdus noiunea de funcie
membru virtual pur.
28
Funcia virtual pur este o funcie membru care este declarat, dar nu este definit n clasa
respectiv. Ea trebuie definit ntr-o clas derivat. Funcia membru virtual pur se declar
n modul urmtor. Antetul obinuit al funciei este precedat de cuvntul cheie virtual, i
antetul se termin cu = 0. Dup cum arat numele i declaraia ei, funcia membru virtual
pur este o funcie virtual, deci selectarea exemplarului funciei din ierarhia de clase se va
face n timpul execuiei programului.
Clasele care conin cel puin o funcie membru virtual pur se vor numi clase abstracte.
Deoarece clasele abstracte conin funcii membru care nu sunt definite, nu se pot crea obiecte
aparinnd claselor abstracte. Dac funcia virtual pur nu s-a definit n clasa derivat atunci
i clasa derivat va fi clas abstract i ca atare nu se pot defini obiecte aparinnd acelei
clase.
S considerm exemplul de mai sus i s scriem un program, care referitor la un porumbel,
urs sau cal determin dac el este gras sau slab, rapid sau ncet, respectiv tnr sau btrn.
Afiarea acestui rezultat se va face de ctre o funcie membru a clasei animal care nu se
suprancarc n clasele derivate. Fiierul abstract1.cpp:
#include <iostream>
using namespace std;
class animal {
protected:
double greutate; // kg
double virsta;
// ani
double viteza;
// km / h
public:
animal( double g, double v1, double v2);
virtual double greutate_medie() = 0;
virtual double durata_de_viata_medie() = 0;
virtual double viteza_medie() = 0;
int gras() { return greutate > greutate_medie(); }
int rapid() { return viteza > viteza_medie(); }
int tanar()
{ return 2 * virsta < durata_de_viata_medie(); }
void afiseaza();
};
animal::animal( double g, double v1, double v2)
{
greutate = g;
virsta = v1;
viteza = v2;
}
void animal::afiseaza()
{
cout << ( gras() ? "gras, " : "slab, " );
cout << ( tanar() ? "tanar, " : "batran, " );
cout << ( rapid() ? "rapid" : "incet" ) << endl;
}
class porumbel : public animal {
public:
porumbel( double g, double v1, double v2):
animal(g, v1, v2) {}
29
Observm c dei clasa animal este clas abstract, este util introducerea ei, pentru c multe
funcii membru pot fi definite n clasa de baz i motenite fr modificri n cele trei clase
derivate.
1.4.5.
Interfee
n limbajul C++ nu s-a definit noiunea de interfa, care exist n limbajele Java sau C#. Dar
orice clas abstract, care conine numai funcii virtuale pure, se poate considera o interfa.
Bineneles, n acest caz nu se vor declara nici date membru n interiorul clasei. Clasa
abstract animal conine att date membru, ct i funcii membru nevirtuale, deci ea nu se
poate considera ca i un exemplu de interfa.
n continuare introducem o clas abstract Vehicul, care nu conine numai funcii membru
virtuale pure, i dou clase derivate din aceast clas abstract. Fiierul vehicul.cpp:
#include <iostream>
using namespace std;
class Vehicul
{
public:
virtual void Porneste() = 0;
30
31
int main()
{
Vehicul *b = new Bicicleta;
Traseu(b);
Vehicul *m = new Masina;
Traseu(m);
delete m;
delete b;
}
n funcia main s-au declarat dou obiecte dinamice de tip Bicicleta, respectiv Masina, i n
acest fel, apelnd funcia Traseu obinem rezultate diferite, dei aceast funcie are ca
parametru formal numai un pointer ctre o clas abstract Vehicul.
1.5.1.
Liste
In limbajul uzual cuvntul list refer o nirare, ntr-o anumit ordine, a unor
nume de persoane sau de obiecte, a unor date etc. Exemple de liste sunt multiple: list de
cumprturi, list de preuri, list de studeni, etc. Ordinea n list poate fi interpretat ca un
fel de legtur ntre elementele listei (dup prima cumprtur urmeaz a doua
cumprtur, dup a doua cumprtur urmeaz a treia cumprtur, etc) sau poate fi vzut
ca fiind dat de numrul de ordine al elementului n list (1-a cumprtur, a 2-a
cumprtur, etc). Tipul de date List care va fi definit n continuare permite
implementarea n aplicaii a acestor situaii din lumea real.
Ca urmare, o list o putem vedea ca pe o secven de elemente < l1 , l 2 ,.., l n > de un
acelai tip (TElement) aflate ntr-o anumit ordine, fiecare element avnd o poziie bine
determinat n cadrul listei. Ca urmare, poziia elementelor n cadrul listei este esenial,
astfel accesul, tergerea i adugarea se pot face pe orice poziie n list. Lista poate fi vzut
ca o colecie dinamic de elemente n care este esenial ordinea elementelor. Numrul n de
elemente din list se numete lungimea listei. O list de lungime 0 se va numi lista vid.
Caracterul de dinamicitate al listei este dat de faptul c lista i poate modifica n timp
lungimea prin adugri i tergeri de elemente n/din list.
In cele ce urmeaz, ne vom referi la listele liniare. O list liniar, este o structur care
fie este vid (nu are nici un element), fie
are un unic prim element;
are un unic ultim element;
fiecare element din list (cu excepia ultimului element) are un singur succesor;
fiecare element din list (cu excepia primului element) are un singur predecesor.
32
Ca urmare, ntr-o list liniar se pot insera elemente, terge elemente, se poate
determina succesorul (predecesorul) unui element, se poate accesa un element pe baza
poziiei sale n list.
O list liniar se numete circular dac se consider predecesorul primului nod a fi
ultimul nod, iar succesorul ultimului nod a fi primul nod.
Conform definiiei anterioare, fiecare element al unei listei liniare are o poziie bine
determinat n list. De asemenea, este important prima poziie n cadrul listei, iar dac se
cunoate poziia unui element din list atunci pe baza aceastei poziii se poate identifica
elementul din list, poziia elementului predecesor i poziia elementului succesor n list
(dac acestea exist). Ca urmare, ntr-o list se poate stabili o ordine ntre poziiile
elementelor n cadrul listei.
Poziia unui element n cadrul listei poate fi vzut n diferite moduri:
1. ca fiind dat de rangul (numrul de ordine al) elementului n cadrul listei. n acest
caz este o similitudine cu tablourile, poziia unui element n list fiind indexul
acestuia n cadrul listei. ntr-o astfel de abordare, lista este vzut ca un tablou
dinamic n care se pot accesa/aduga/terge elemente pe orice poziie n list.
2. ca fiind dat de o referin la locaia unde se stocheaz elementul listei (ex:
pointer spre locaia unde se memoreaz elementul).
Pentru a asigura generalitatea, vom abstractiza noiunea de poziie a unui element n
list i vom presupune c elementele listei sunt accesate prin intermediul unei poziii
generice.
Vom spune c o poziie p ntr-o list este valid dac este poziia unui element al
listei. Spre exemplu, dac p ar fi un pointer spre locaia unde se memoreaz un element al
listei, atunci p este valid dac este diferit de pointerul nul sau de orice alt adres care nu
reprezint adresa de memorare a unui element al listei. n cazul n care p ar fi rangul (numrul
de ordine al) elementului n list, atunci p este valid dac nu depete numrul de elemente
din list.
Ca urmare, dac ne gndim la o list liniar n care operaiile de
acces/inserare/tergere s se fac pe baza unei poziii generice n list, se ajunge la urmtorul
tip abstract de date.
Lista vid o vom nota n ceea ce urmeaz cu .
Tipul Abstract de Date LISTA
domeniu
L={l | l este o list cu elemente de tip TElement}
operaii (interfaa minimal)
creeaz(l)
descriere: se creeaz o list vid
pre: adevrat
33
post: l
L , l =
adaugSfarsit (l, e)
descriere: se adaug un element la sfritul listei
pre: l
L, e
TElement
L, l este l n care a fost adugat e la sfrit
post: l
adaugInceput(l, e)
descriere: se adaug un element la nceputul listei
pre: l
L, e
TElement
L, l este l n care a fost adugat e la nceput
post: l
valid(l, p)
descriere: funcie care verific dac o poziie n list este valid
pre: lL, p e o poziie n l
post: valid= adevrat
dac p este o poziie valid n l
fals
n caz contrar
adaugnainte(l, p, e)
descriere: se adaug un element naintea unei anumite poziii n list
pre: l
L, e
TElement, p e o poziie n l, valid(l, p)
L, l este l n care a fost inserat e nainte de poziia p
post: l
adaugDup(l, p, e)
descriere: se adaug un element dup o anumit poziie n list
L, e
TElement, p e o poziie n l, valid(l, p)
pre: l
post: l
L, l este l n care a fost inserat e dup poziia p
terge (l, p, e)
descriere: se terge elementul din list situat pe o anumit poziie
pre: l
L, e
TElement, p e o poziie n l, valid(l, p)
TElement, l
L, l este l din care a fost ters elementul de pe poziia
post: e
p, e este elementul ters
element (l, p, e)
descriere: accesarea elementului din list de pe o anumit poziie
pre: l
L, e
TElement, p e o poziie n l, valid(l, p)
TElement, e este elementul de pe poziia p din l
post: e
modifica (l, p, e)
descriere: modificarea elementului din list de pe o anumit poziie
pre: l
L, e
TElement, p e o poziie n l, valid(l, p)
post: l
L, l este l n care s-a nlocuit elementul de pe poziia p cu e
prim(l)
descriere: funcie care returneaz poziia primului element n list
L
pre: l
post: prim= poziia primului element din l sau o poziie care nu e valid
dac l e vid
ultim(l)
descriere: funcie care returneaz poziia ultimului element n list
34
L
pre: l
post: ultim= poziia ultimului element din l sau o poziie care nu e valid
dac l e vid
urmtor(l, p)
descriere: funcie care returneaz poziia n list urmtoare unei poziii
date
pre: l
L, p e o poziie n l, valid(l, p)
post: urmator= poziia din l care urmeaz poziiei p sau o poziie care nu e
valid dac p e poziia ultimului element din list
precedent(l, p)
descriere: funcie care returneaz poziia n list precedent unei poziii
date
pre: l
L, p e o poziie n l, valid(l, p)
post: precedent= poziia din l care precede poziia p sau o poziie care nu
e valid dac p e poziia primului element din list
caut(l, e)
descriere: funcie care caut un element n list
pre: l
L, e
TElement
post: caut = prima poziie pe care apare e n l sau o poziie care nu e
valid dac e l
apare(l, e)
descriere: funcie care verific apartenena unui element n list
pre: l
L, e
TElement
post: apare = adevrat e l
fals
contrar
vid(l)
descriere: funcie care verific dac lista este vid
L
pre: l
post: vid =
adevrat
fals
dim(l)
descriere: funcie care returneaz numrul de elemente din list
pre: l
L
post: dim=numrul de elemente din list
iterator(l, i)
descriere: se construiete un iterator pe list
L
pre: l
post: i este un iterator pe lista l
distruge(l)
descriere: distruge o list
L
pre: l
post: lista l a fost distrus
Reamintim modul n care va putea fi tiprit o list (ca orice alt container care poate fi
iterat) folosind iteratorul construit pe baza operaiei iterator din interfaa listei.
35
Observaie
Menionm faptul c nu este o modalitate unanim acceptat pentru specificarea
operaiilor. Spre exemplu, pentru operaia adaugSfarsit din interfaa TAD Lista, o alt
modalitate corect de specificare ar fi una dintre cele de mai jos:
adaugSfarsit (l, e)
desc.: se adaug un element la sfritul listei
pre: l
L, e
TElement
L, l = l {e}, e este pe ultima poziie n l
post: l
adaugSfarsit (l, e)
descriere: se adaug un element la sfritul listei
pre: l
L, e
TElement
L, l este modificat prin adugarea lui e la sfrit i pstrarea celorlate
post: l
elemente pe poziiile lor
1.5.2.
Dicionare
Dicionarele reprezint containere coninnd elemente sunt forma unor perechi (cheie,
valoare). Dicionarele pstreaz elemente n aa fel nct ele s poat fi uor localizate
folosind chei. Operaiile de baz pe dicionare sunt cutare, adugare i tergere elemente.
ntr-un dicionar cheile sunt unice i n general, o cheie are o unic valoare asociat.
D={d | d
post: m
M, m este mulimea cheilor din dicionarul d
valori(d, c)
37
Reamintim modul n care va putea fi tiprit un dicionar (ca orice alt container care
poate fi iterat) folosind iteratorul construit pe baza operaiei iterator din interfaa
dicionarului.
Subalgoritmul tiprire(d) este:
{pre: d este un dicionar}
{post: se tipresc elementele dicionarului}
iterator(d, i)
{se obine un iterator pe dicionarul d}
Cttimp valid(i) execut
{ct timp iteratorul e valid}
element(i, e) {e este elementul curent referit de iterator}
@ tiprete e
{se tiprete elementul curent}
urmtor(i)
{iteratorul refer urmtorul element}
sfct
sf-tiprire
Se pot folosi biblioteci existente pentru structuri de date (Python, C++, Java, C#). Nu se
cere implementare pentru operaiile listei.
2. Scriei un program ntr-unul din limbajele de programare Python, C++, Java, C# care:
a. Definete o clas B avnd un atribut b de tip ntreg i o metod de tiprire care
afieaz atributul b la ieirea standard.
b. Definete o clas D derivat din B avnd un atribut d de tip ir de caractere i de
asemenea o metod de tiprire pe ieirea standard care va afia atributul b din
clasa de baz i atributul d.
c. Definete o funcie care construiete un dicionar coninnd: un obiect o1 de tip B
avnd b egal cu 8; un obiect o2 de tip D avnd b egal cu 5 i d egal cu D5; un
obiect o3 de tip B avnd b egal cu -3; un obiect o4 de tip D avnd b egal cu 9 i d
egal cu D9. (cheia unui obiect din dicionar este valoarea b, iar valoarea
asociat cheii este obiectul).
d. Definete o funcie care primete un dicionar cu obiecte de tip B i verific dac
n dicionar exist un obiect care satisface proprietatea: b>6.
e. Pentru tipul de dat dicionar utilizat n program, scriei specificaiile operaiilor
folosite.
Se pot folosi biblioteci existente pentru structuri de date (Python, C++, Java, C#). Nu se
cere implementare pentru operaiile dicionarului.
39
2. Baze de date
2.1. Baze de date relaionale. Primele trei forme normale ale unei
relaii
2.1.1.
Modelul relaional
i poate fi considerat ca o mulime de vectori cu cte n valori, cte o valoare pentru fiecare
din atributele Ai. O astfel de relaie se poate memora printr-un tabel de forma:
R
A1
... Ai
..
An
r1
a11
... a1j
... a1n
...
...
... ...
... ...
ri
ai1
... aij
... ain
...
...
... ...
... ...
unde liniile din acest tabel formeaz elementele relaiei, sau tupluri, sau nregistrri, care
n general sunt distincte, i aij D j , j = 1,..., n, i = 1,..., m. Deoarece modul n care se
evideniaz elementele relaiei R de mai sus seamn cu un tabel, relaia se mai numete i
tabel. Pentru a pune n eviden numele relaiei (tabelului) i lista atributelor vom nota
aceast relaie cu:
R[A1 , A2 ,..., An ].
Modelul relaional al unei baze de date const dintr-o colecie de relaii ce variaz n
timp (coninutul relaiilor se poate schimba prin operaii de adugare, tergere i actualizare).
O baz de date relaional const din trei pri:
1. Datele (relaii sau tabele, legturi ntre tabele) i descrierea acestora;
40
NUME
ANUL_NASTERII ANUL_DE_STUDIU
Pop Ioan
1985
2
Barbu Ana
1987
1
Dan Radu
1986
3
Exemplul 2. CARTE [AUTORI, TITLU, EDITURA, AN_APARITIE],
cu valorile:
AUTORI
Date, C.J.
TITLU
EDITURA
AN_APARITIE
An Introduction to Database
Addison-Wesley
2004
Systems
Publishing Comp.
2011
Ullman, J.,
A First Course in Database Systems Addison-Wesley +
Prentice-Hall
Widom, J.
Helman, P.
The Science of Database
Irwin, SUA
1994
Management
Ramakrishnan, R. Database Management
McGraw-Hill
2007
Systems
Pentru fiecare relaie se poate preciza un atribut sau o colecie de atribute, din cadrul
relaiei, numit cheie, cu rol de identificare a elementelor relaiei (cheia ia valori diferite
pentru nregistrri diferite din relaie, deci fiecare nregistrare se poate identifica prin
valoarea cheii). Dac se d cte o valoare pentru atributele din cheie, se poate determina linia
(una singur) n care apar aceste valori. Vom presupune c nici o submulime de atribute din
cheie nu este cheie. Deoarece toate elementele relaiei sunt diferite, o cheie exist totdeauna
(n cel mai ru caz cheia este format din toate atributele relaiei). Pentru exemplul 1 se poate
alege NUME ca i cheie (atunci n baza de date nu pot exista doi studeni cu acelai nume),
iar pentru exemplul 2 se poate alege grupul de atribute {AUTORI, TITLU, EDITURA,
AN_APARITIE} ca i cheie, sau s se introduc un nou atribut (de exemplu COTA) pentru
identificare.
Pentru anumite relaii pot fi alese mai multe chei. Una dintre chei (un atribut simplu sau
un atribut compus din mai multe atribute simple) se alege cheie principal (primar), iar
celelalte se vor considera chei secundare. Sistemele de gestiune a bazelor de date nu permit
existena a dou elemente distincte ntr-o relaie cu aceeai valoare pentru oricare cheie
(principal sau secundar), deci precizarea unei chei constituie o restricie pentru baza de
date.
41
atributului A se caut printre valorile cheii din relaia R1. Cele dou relaii R1 i R2 nu este
obligatoriu s fie distincte.
R1
A=cheie extern
R2
chei
Exemplu:
CLASE [cod, profil]
ELEVI [nrmatricol, nume, clasa, datanasterii].
Legtura o putem stabili ntre relaia CLASE (considerat ca printe pentru legtur) i
relaia ELEVI (ca membru pentru legtur) prin egalitatea CLASE.cod=ELEVI.clasa. Unei
anumite clase (memorat n relaia CLASE), identificat printr-un cod, i corespund toi
elevii din clasa cu codul respectiv.
Prin cheie extern se pot memora legturi 1:n ntre entiti: la o clas corespund
orici elevi, iar unui elev i este asociat cel mult o clas.
Cheia extern se poate folosi i pentru a memora legturi m:n ntre entiti.
Fie dou entiti: discipline i studeni. La o disciplin sunt "inscrii" mai muli studeni, iar
un student are asociate mai multe discipline. Varianta de memorare cuprinde o relaie
intermediar.
Pentru ca valorile dintr-o baz de date s fie corecte, la definirea bazei de date se pot
preciza anumite restricii de intergritate (ele sunt verificate de sistemul de gestiune a bazei
de date la modificarea datelor din tabele). Aceste restricii se refer la o coloan, la un tabel,
la o legtur ntre dou tabele:
42
2.1.2.
In general anumite date se pot reprezenta n mai multe moduri prin relaii (la modelul
relaional). Pentru ca aceste date s se poat prelucra ct mai simplu (la o operaie de
actualizare a datelor s nu fie necesare teste suplimentare) este necesar ca relaiile n care se
memoreaz datele s verifice anumite condiii (s aib un anumit nivel de normalizare).
Pn n prezent se cunosc mai multe forme normale pentru relaii, dintre care cele mai
cunoscute sunt: 1NF, 2NF, 3NF, BCNF, 4NF, 5NF. Avem urmtoarele incluziuni pentru
relaii n diferite forme normale:
1NF
2NF
3NF
BCNF
4NF
5NF
Dac o relaie nu este de o anumit form normal, atunci ea se poate descompune n
mai multe relaii de aceast form normal.
i1 , Ai2
,..., Ai p
}( R ), ,
43
R S [ , , ] =
{( (r), (r ), (s)) r R, s S
si
(r ) = (s)}.
SC
r1
r2
r3
Student
s1
s2
s1
CadruDidactic
c1
c2
c2
CD
r1
r2
r3
CadruDidactic
c1
c2
c2
Disciplina
d1
d2
d3
Observaie. Prin atribut simplu vom nelege un atribut oarecare din relaie, iar prin
atribut compus vom nelege o mulime de atribute (cel puin dou) din relaie.
44
Este posibil ca n diverse aplicaii practice s apar atribute (simple sau compuse) ce
iau mai multe valori pentru un element din relaie. Aceste atribute formeaz un atribut
repetitiv.
NUME
Pop Ioan
NOTA
10
9
8
8
7
10
9
Grupurile repetitive creaz foarte multe greuti n memorarea diverselor relaii i din
aceast cauz se ncearc evitarea lor, fr ns a pierde date. Dac R[A] este o relaie, unde
A este mulimea atributelor, iar formeaz un grup repetitiv (atribut simplu sau compus),
atunci R se poate descompune n dou relaii fr ca s fie atribut repetitiv. Dac C este o
cheie pentru relaia R, atunci cele dou relaii n care se descompune relaia R sunt:
R' [C ] = C (R ) i R' ' [A ] = A (R ) .
Exemplul 6. Relaia STUDENT din exemplul 4 se descompune n urmtoarele dou
relaii:
DATE_GENERALE [NUME, ANULNASTERII, GRUPA],
REZULTATE [NUME, DISCIPLINA, NOTA].
Exemplul 7. Relaia CARTE din exemplul 5 se descompune n urmtoarele trei relaii
(n relaia CARTE exist dou grupuri repetitive):
CARTI [Cota, Titlu, Editura, AnApariie, Limba],
AUTORI [Cota, NumeAutor],
CUVINTE_CHEIE [Cota, CuvntCheie].
Observaie. Dac o carte nu are autori sau cuvinte cheie asociate, atunci ea va avea
cte o nregistrare n relaiile AUTORI sau CUVINTE_CHEIE n care al doilea atribut are
valoarea null. Dac se dorete eliminarea acestor nregistrri, atunci relaia CARTE nu se va
putea obine din cele trei relaii numai prin join natural (sunt necesari operatori de join
extern).
Definiie. O relaie este de prima form normal (1NF) dac ea nu conine grupuri
(de atribute) repetitive.
45
Examen
1
2
3
4
5
NumeStudent
Alb Ana
Costin Constantin
Alb Ana
Enisei Elena
Frian Florin
Disciplina
Matematic
Istorie
Istorie
Matematic
Matematic
Nota
10
9
8
9
10
CadruDidactic
Rus Teodor
Popa Horea
Popa Horea
Rus Teodor
Rus Teodor
Dac pstrm o astfel de dependen funcional, atunci pot apare urmtoarele probleme:
Risip de spaiu: aceleai asocieri se memoreaz de mai multe ori. Legtura dintre
disciplina de Matematic i profesorul Rus Teodor este memorat de trei ori, iar dintre
disciplina Istorie i profesorul Popa Horea se memoreaz de dou ori.
Anomalii la actualizare: schimbarea unei date ce apare ntr-o asociere implic efectuarea
acestei modificri n toate asocierile (fr a se ti cte astfel de asocieri exist), altfel baza
46
Anomalii la tergere: la tergerea unor nregistrri se pot terge i asocieri (ntre valori)
ce nu se pot reface. De exemplu, dac se terg nregistrrile 2 i 3, atunci asocierea dintre
Disciplina i CadruDidactic se pierde.
Anomaliile de mai sus apar datorit existenei unei dependene funcionale ntre
mulimi de atribute. Pentru a elimina situaiile amintite trebuie ca aceste dependene
(asocieri) de valori s se pstreze ntr-o relaie separat. Pentru aceasta este necesar ca relaia
iniial s se descompun, fr ca prin descompunere s se piard date sau s se introduc
date noi prin compunerea de relaii (trebuie ca descompunerea "s fie bun"). O astfel de
descompunere se face n momentul proiectrii bazei de date, cnd se pot stabili dependenele
funcionale.
3. Dac , atunci , cu .
( r1 ) = ( r2 ) ( r1 ) = ( r2 ) ( r1 ) = ( r2 )
Definiie. Un atribut A (simplu sau compus) se numete prim dac exist o cheie C i
A C (C este o cheie compus, sau A este chiar o cheie a relaiei). Dac un atribut nu este
inclus n nici o cheie, atunci se numete neprim.
Definiie. Fie R[A1 , A2 ,..., An ] i , {A1 , A2 ,..., An } . Atributul este complet
dependent funcional de dac este dependent funcional de (deci ) i nu
este dependent funcional de nici o submulime de atribute din ( , nu este
adevrat).
Observaie. Dac atributul nu este complet dependent funcional de (deci
este dependent de o submulime a lui ), atunci este un atribut compus.
47
orice atribut neprim (simplu sau compus) (deci care nu este inclus ntr-o cheie) este
complet dependent funcional de oricare cheie a relaiei.
Observaie. Dac o relaie este de prima form normal (1NF) i nu este de a doua
form normal (2NF), atunci are o cheie compus (dac o relaie nu este de a doua form
normal, atunci exist o dependen funcional cu atribut inclus ntr-o cheie).
Pentru a preciza modul de descompunere pentru cazul general, fie R[A1, A2 ,..., An ] o
relaie i C A = {A1 , A2 ,..., An } o cheie. Presupunem c exist A , C = ( este
un atribut necheie), dependent funcional de C ( este complet dependent
funcional de o submulime strict de atribute din cheie). Dependena se poate
elimina dac relaia R se descompune n urmtoarele dou relaii:
48
{CadruDidIndr} {Departament}.
Din existena acestei dependene funcionale se deduce c relaia nu este de 3NF.
Pentru a elimina dependena funcional, relaia se poate descompune n urmtoarele dou
relaii:
{CodPostal} {LocalitateDomiciliu}.
Deoarece exist aceast dependen funcional, deducem c relaia ADRESE nu este de a
treia form normal, deci este necesar descompunerea ei.
Definiie. O relaie este n 3NF Boyce-Codd, sau BCNF, dac orice determinant
(pentru o dependen funcional) este cheie, deci nu exist dependene funcionale
astfel nct s nu fie cheie.
49
Pentru a elimina dependena funcional amintit mai sus trebuie s facem urmtoarea
descompunere pentru relaia PLANIFICARE_EX:
relaie
relaie
=
<>
(condiie)
NOT condiie
condiie1 AND condiie2
condiie1 OR condiie2
unde condiie, condiie1, condiie2 sunt condiii de tipurile 1-4.
In primul tip de condiie apare construcia 'valoare', care poate fi una din tipurile
urmtoare. Pentru fiecare construcie se ia n valoare o anumit relaie curent, care rezult
din contextul n care apare aceasta.
50
expresie - dac se evalueaz expresia, iar dac apar i denumiri de atribute, atunci acestea
se iau dintr-o nregistrare curent.
COUNT
SUM
MIN
nregistrrile din relaia curent. La determinarea acestei valori se iau toate valorile
atributului precizat ca argument (din toate nregistrrile), sau numai valorile distincte,
dup cum lipsete sau apare cuvntul DISTINCT. Valorile astfel determinate sunt:
numrul de valori (pentru COUNT), suma acestor valori (apare SUM, valorile trebuie s
fie numerice), valoarea medie (apare AVG, valorile trebuie s fie numerice), valoarea
maxim (apare MAX), respectiv valoarea minim (apare MIN).
In continuare se vor preciza operatorii care se pot folosi pentru interogarea bazelor
de date relaionale.
Selecia (sau proiecia orizontal) a unei relaii R - determin o nou relaie ce are aceeai
schem cu a relaiei R. Din relaia R se iau numai nregistrrile care ndeplinesc o
condiie c. Notaia pentru acest operator este: c ( R ) .
Proiecia (sau proiecia vertical) - determin o relaie nou ce are atributele precizate
printr-o mulime de atribute. Din fiecare nregistrare a unei relaii R se determin numai
valorile atributelor incluse n mulimea . Mulimea de atribute se poate extinde la o
mulime de expresii (n loc de o mulime de atribute), care precizeaz coloanele relaiei
care se construiete. Notaia pentru acest operator este: ( R) .
Produsul cartezian a dou relaii: R1R2 - care determin o relaie nou ce are ca
atribute concatenarea atributelor din cele dou relaii, iar fiecare nregistrare din R1 se
concateneaz cu fiecare nregistrare din R2.
Reuniunea, diferena i intersecia a dou relaii: R1 R2, R1 - R2, R1 R2. Cele dou
relaii trebuie s aib aceeai schem.
Joinul condiional sau theta join, notat prin R1 R2 - care determin acele
nregistrri din produsul cartezian al celor dou relaii care ndeplinesc o anumit
condiie. Din definiie se observ c avem: R1 R2= ( R1 R2 ) .
Joinul natural, notat prin R1 R2, care determin o relaie nou ce are ca atribute
reuniunea atributelor din cele dou relaii, iar nregistrrile se obin din toate perechile
de nregistrri ale celor dou relaii care au aceleai valori pentru atributele comune.
Dac cele dou relaii au schemele R1 [ ], R2 [ ] , i = {A1 , A2 ,..., An }, atunci
joinul natural se poate calcula prin construcia urmtoare:
R1 R2 =
R1
R2
51
Joinul extern stnga, notat (n acest material) prin R1 > C R2 , determin o relaie nou
ce are ca atribute concatenarea atributelor din cele dou relaii, iar nregistrrile se obin
astfel: se iau nregistrrile care se obin prin joinul condiional R1 c R2, la care se
adaug nregistrrile din R1 care nu s-au folosit la acest join condiional combinate cu
valoarea null pentru toate atributele corespunztoare relaiei R2.
Joinul extern dreapta, notat prin R1 < C R2 , se obine asemntor ca joinul extern
stnga, dar la nregistrrile din R1 c R2 se adaug nregistrrile din R2 care nu s-au
folosit la acest join condiional combinate cu valoarea null pentru toate atributele
corespunztoare relaiei R1.
R1
cu , i se noteaz prin R1 R2 [ ].
Deducem c atributele din ct sunt date de
mulimea . O nregistrare r R1 R2
dac r2 R2 , r1 R1 ce ndeplinete
condiiile:
1.
2.
r1
r2
r
r2
(r1 ) = r ;
r2
R2
(r1 ) = r2 .
R1 R2 = R1 ( R1 R2 ) ;
R1 c R2= c ( R1 R2 ) ;
;
R1
R
2
52
(R
C R 2 ) ] R3 .
(R
C R2 ) ].
(r1 ) = r
(r1 ) = r2 .
( R1 ) . De aici obinem:
R1 R2 = ( R1 )
((
( R1 ) R2 R1 .
La lista de operatori relaionali amintii mai sus se pot aminti cteva instruciuni utile la
rezolvarea unor probleme:
Atribuirea: unei variabile (relaii) R i vom atribui o relaie dat printr-o expresie
construit cu operatorii de mai sus. In instruciune se poate preciza, pentru R, i
denumirea coloanelor.
R[lista] := expresie
Gruparea: {lista1} group by {lista 2} ( R ) , care este o extensie pentru proiecie. Inregistrrile din R
sunt grupate dup coloanele din lista2, iar pentru un grup de nregistrri cu aceleai valori
pentru lista2 se evalueaz lista1 (unde pot apare funcii de grupare).
53
ALL
SELECT DISTINCT
exp [AS cmp ] [exp [AS cmp ]]...
TOP n [PERCENT]
FROM sursa1 [alias] [,sursa2 [alias]]...
[WHERE condiie]
[GROUP BY lista_cmpuri [HAVING condiie]]
UNION [ALL]
cmp ASC
cmp ASC
ORDER BY
, ORDER BY
...
nrcmp DESC
nrcmp DESC
Aceast comand selecteaz date din sursele de date precizate n clauza FROM. Pentru
precizarea (calificarea) cmpurilor (dac este necesar, deci dac folosirea numai a numelui
cmpului produce ambiguitate, adic exist mai multe cmpuri cu acest nume n sursele de
date) se poate folosi numele tabelului sau un nume sinonim (alias local numai n comanda
SELECT) stabilit n FROM dup numele sursei de date. Dac se definete un "alias", atunci
calificarea se face numai cu el (nu se va mai face cu numele tabelului).
O construcie numit sursa poate fi:
1. un tabel sau view din baza de date
2. (instruciune_select)
3. expresie_join, sub forma:
sursa1 [alias] operator_join sursa2 [alias] ON condiie_legatur
(expresie_join)
(condiie)
54
INNER
LEFT [OUTER]
Sursa1
JOIN Sursa2 ON conditie
RIGHT [OUTER]
FULL [OUTER]
Joinul condiional, din algebra relaional, notat prin Sursa1 c Sursa2, este precizat
prin Sursa1 INNER JOIN sursa2 ON condiie, i determin acele nregistrri din produsul
cartezian al celor dou surse care ndeplinesc condiia din ON.
Joinul extern stnga, precizat prin Sursa1 LEFT [OUTER] JOIN sursa2 ON
condiie, determin o surs nou ce are ca atribute concatenarea atributelor din cele dou
surse, iar nregistrrile se obin astfel: se iau nregistrrile care se obin prin joinul condiional
Sursa1 c Sursa2, la care se adaug nregistrrile din sursa1 care nu s-au folosit la acest join
condiional combinate cu valoarea null pentru toate atributele corespunztoare din Sursa2.
Joinul extern dreapta, precizat prin Sursa1 RIGHT [OUTER] JOIN sursa2 ON
condiie, determin o surs nou ce are ca atribute concatenarea atributelor din cele dou
surse, iar nregistrrile se obin astfel: se iau nregistrrile care se obin prin joinul condiional
Sursa1 c Sursa2, la care se adaug nregistrrile din sursa2 care nu s-au folosit la acest join
condiional combinate cu valoarea null pentru toate atributele corespunztoare din Sursa1.
Joinul extern total, precizat prin Sursa1 FULL [OUTER] JOIN sursa2 ON condiie,
se obine prin reuniunea rezultatelor obinute de joinul extern stnga i joinul extern dreapta.
Alte tipuri de expresii join:
Dac n clauza FROM apar mai multe surse de date (care se vor evalua la un tabel),
atunci ntre un astfel de tabel - pe care l vom numi tabel principal, i celelalte tabele este
indicat s existe anumite legturi (stabilite prin condiii). Plecnd de la fiecare nregistrare a
tabelului principal se determin nregistrrile din celelalte tabele asociate prin astfel de
legturi (deci nregistrrile ce verific o condiie). Dac legtura (condiia) nu se stabilete,
atunci se consider c ea asociaz toate nregistrrile din celelalte tabele pentru fiecare
nregistrare a tabelului principal (se consider c valoarea condiiei este true atunci cnd ea
lipsete). Aceast condiie de legtur dintre sursele de date se precizeaz prin:
WHERE condiie_filtrare
55
(subselectie )
Se verific dac valoarea expresiei apare (sau nu - cu NOT) ntr-o list de valori sau ntro subselecie. O subselecie este o surs de date generat cu comanda SELECT i care
are numai un singur cmp - cu valori de acelai tip cu valorile expresiei. Aceast condiie
corespunde testului de "apartenen" al unui element la o mulime.
ALL
Valorile cmpului din stnga operatorului relaional i valorile singurului cmp din
subselecie trebuie s fie de acelai tip. Se obine valoarea adevrat pentru condiie dac
valoarea din partea stng este n relaia dat de operator pentru:
56
O condiie elementar poate avea una din valorile: true, false, null. Valoarea null se
obine dac unul din operanzii utilizai are valoarea null. Valorile de adevr pentru operatorii
not, and, or sunt date n continuare:
true false null
false true null
not
and
true
false
null
true
true
false
null
false
false
false
false
null
null
false
null
or
true
false
null
true
true
true
true
false
true
false
null
null
true
null
Din aceast succesiune de valori se pot selecta toate cmpurile din toate tabelele (apare
"*" dup numele comenzii), sau se pot construi cmpuri ce au ca valoare rezultatul unor
expresii. Cmpurile cu aceeai denumire n tabele diferite se pot califica prin numele sau
alias-ul tabelelor surs. Numele cmpului sau expresiei din tabelul rezultat este stabilit
automat de sistem (n functie de expresia ce-l genereaz), sau se poate preciza prin clauza AS
ce urmeaz expresiei (sau cmpului). In acest fel putem construi valori pentru o nou
nregistrare ntr-un tabel_final.
Expresiile se precizeaz cu ajutorul operanzilor (cmpuri, rezultatul unor funcii) i a
operatorilor corespunztori tipurilor de operanzi.
In tabelul final se pot include toate sau numai o parte din nregistrri, dup cum e
precizat printr-un predicat ce apare n faa listei de coloane::
ALL - toate nregistrrile
DISTINCT - numai nregistrrile distinte
TOP n - primele n nregistrri
TOP n PERCENT - primele n% nregistrri
Inregistrrile din "tabelul final" se pot ordona cresctor (ASC) sau descresctor
(DESC) dup valorile unor cmpuri, precizate n clauza ORDER BY. Cmpurile se pot
preciza prin nume sau prin poziia lor (numrul cmpului) n lista de cmpuri din comanda
SELECT (precizarea prin poziie este obligatorie atunci cnd se dorete sortarea dup
valorile unei expresii). Ordinea cmpurilor din aceast clauz precizeaz prioritatea cheilor
de sortare.
Mai multe nregistrri consecutive din "tabelul final* pot fi grupate ntr-o singur
nregistrare, deci un grup de nregistrri se nlocuiete cu o singur nregistrare. Un astfel de
grup este precizat de valorile comune ale cmpurilor ce apar n clauza GROUP BY.
"Tabelul nou" se sorteaz (automat de ctre sistem) creasctor dup valorile cmpurilor din
GROUP BY. Inregistrrile consecutive din fiierul astfel sortat, ce au aceeai valoare pentru
toate cmpurile din GROUP BY, se nlocuiesc cu o singur nregistrare. Prezena unei astfel
de nregistrri poate fi condiionat de valoarea adevrat pentru o condiie ce se trece n
clauza HAVING.
Pentru grupul de nregistrri astfel precizat (deci pentru o mulime de valori) se pot
folosi urmtoarele funcii:
ALL
cmp sau AVG([ALL]) expresie)
AVG
DISTINCT
Pentru grupul de nregistrri se iau toate valorile (cu ALL, care este i valoarea
implicit) sau numai valorile distincte (apare DISTINCT) ale cmpului sau expresiei
57
ALL
COUNT
cmp
DISTINCT
Aceast funcie determin numrul de nregistrri din grup (apare '*'), numrul de valori
ale unui cmp (apare ALL, identic cu '*'), sau numrul de nregistrri distincte din grup
(cu DISTINCT).
ALL
cmp sau SUM([ALL]) expresie)
SUM
DISTINCT
Pentru nregistrrile din grup se face suma valorilor unui cmp sau ale unei expresii
numerice (deci numrul de termeni este dat de numrul de nregistrri din grup) sau suma
valorilor distincte ale cmpului.
MAX
MAX ALL
([ALL]) expresie)
cmp sau
MIN
MIN DISTINCT
Pentru fiecare nregistrare din grup se determin valoarea unei expresii sau cmp i se
afl valoarea maxim sau minim dintre aceste valori.
Cele cinci funcii amintite mai sus (AVG, COUNT, SUM, MIN, MAX) pot apare att
n expresiile ce descriu cmpurile din fiierul rezultat, ct i n clauza HAVING. Deoarece
aceste funcii se aplic unui grup de nregistrri, n comada SELECT acest grup trebuie
generat de clauza GROUP BY. Dac aceast clauz lipsete, atunci ntregul "tabel final"
constituie un grup, deci tabelul rezultat va avea o singur nregistrare.
In general nu este posibil selectarea cmpurilor singure (fr rezultat al funcilor
amintite) dect numai dac au fost trecute n GROUP BY. Dac totui apar, i aceast
folosire nu produce eroare, atunci se ia o valoare oarecare, pentru o nregistrare din grup.
Dou tabele cu acelai numr de cmpuri (coloane) i cu acelai tip pentru valorile
cmpurilor aflate pe aceleai poziii se pot reuni ntr-un singur tabel obinut cu ajutorul
operatorului UNION. Din tabelul rezultat obinut se pot pstra toate nregistrrile (apare
ALL) sau numai cele distincte (nu apare ALL). Clauza ORDER BY poate apare numai
pentru ultima selecie. Nu se pot combina subselecii prin clauza UNION.
Intre dou rezultate (mulimi de nregistrri) obinute cu instruciuni SELECT se poate
folosi operatorul INTERSEC sau EXCEPT (sau MINUS).
Clauzele din instruciunea SELECT trebuie s fie n ordinea: lista_expresii FROM ...
WHERE ... HAVING ... ORDER BY ...
O comand se poate memora n baza de date ca o component numit view. Definirea
este:
CREATE VIEW nume_view AS comanda_SELECT
58
59
3.1.1.1.
Partiii i Blocuri
Un sistem de fiiere Unix este gzduit fie pe un periferic oarecare (hard-disc, CD, dischet
etc.), fie pe o partiie a unui hard-disc. Partiionarea unui hard-disc este o operaie
(relativ) independent de sistemul de operare ce va fi gzduit n partiia respectiv. De
aceea, att partiilor, ct i suporturilor fizice reale le vom spune generic, discuri Unix.
Un fiier Unix este o succesiune de octei, fiecare octet putnd fi adresat n mod individual.
Este permis att accesul secvenial, ct i cel direct. Unitatea de schimb dintre disc i
memorie este blocul. La sistemele mai vechi acesta are 512 octei, iar la cele mai noi pn la
4Ko, pentru o mai eficient gestiune a spaiului. Un sistem de fiiere Unix este o structur de
date rezident pe disc. Aa dup cum se vede din figura de mai sus, un disc este compus din
patru categorii de blocuri.
Blocul 0 conine programul de ncrcare al SO. Acest program este dependent de maina sub
care se lucreaz.
Blocul 1 este numit i superbloc. In el sunt trecute o serie de informaii prin care se
definete sistemul de fiiere de pe disc. Printre aceste informaii amintim:
-numrul n de inoduri (detaliem imediat);
-numrul de zone definite pe disc;
-pointeri spre harta de bii a alocrii inodurilor;
-pointeri spre harta de bii a spaiului liber disc;
-dimensiunile zonelor disc etc.
Blocurile 2 la n, unde n este o constant a formatrii discului. Un inod (sau i-nod) este
numele, n terminologia Unix, a descriptorului unui fiier. Inodurile sunt memorate pe
60
disc sub forma unei liste (numiti-list). Numrul de ordine al unui inod n cadrul i-listei se
reprezint pe doi octei i se numete i-numr. Acest i-numr constituie legtura dintre fiier
i programele utilizator.
Partea cea mai mare a discului este rezervat zonei fiierelor. Alocarea spaiului pentru fiiere
se face printr-o variant elegant de indexare. Informaiile de plecare pentru alocare sunt
fixate n inoduri.
3.1.1.2.
Directori i I-noduri
Structura unei intrri ntr-un fiier director este ilustrat n figura de mai jos
Deci, n director se afl numele fiierului i referina spre inodul descriptor al fiierului.
Un inod are, de regul, ntre 64 i 128 de octei i el conine informaiile din tabelul urmtor:
3.1.1.3.
Fiecare sistem de fiiere Unix are cteva constante proprii, printre care amintim:
lungimea unui bloc, lungimea unui inod, lungimea unei adrese disc, cte adrese de prime
blocuri se nregistreaz direct n inod i cte referine se trec n lista de referine indirecte.
Indiferent de valorile acestor constante, principiile de nregistrare / regsire sunt aceleai.
Pentru fixarea ideilor, vom alege aceste constante cu valorile ntlnite mai frecvent la
sistemele de fiiere deja consacrate. Astfel, vom presupune c un bloc are lungimea de 512
octei. O adres disc se reprezint pe 4 octei, deci ntr-un bloc se pot nregistra 128 astfel de
se adrese. In inod trec direct primele 10 adrese de blocuri, iar lista de adrese indirecte are
3 elemente. Cu aceste constante, n figura de mai jos este prezentat structura pointerilor
spre blocurile ataate unui fiier Unix.
61
In inodul fiierului se afl o list cu 13 intrri, care desemneaz blocurile fizice aparinnd
fiierului.
Primele 10 intrri conin adresele primelor 10 blocuri de cte 512 octei care aparin
fiierului.
Intrarea nr. 11 conine adresa unui bloc, numit bloc de indirectare simpl. El conine
adresele urmtoarelor 128 blocuri de cte 512 octei, care aparin fiierului.
Intrarea nr. 12 conine adresa unui bloc, numit bloc de indirectare dubl. El conine adresele
a 128 blocuri de indirectare simpl, care la rndul lor conin, fiecare, adresele a cte 128
blocuri, de 512 octei fiecare, cu informaii aparinnd fiierului.
Intrarea nr. 13 conine adresa unui bloc, numit bloc de indirectare tripl. In acest
bloc sunt coninute adresele a 128 blocuri de indirectare dubl, fiecare dintre acestea
coninnd adresele a cte 128 blocuri de indirectare simpl, iar fiecare dintre acestea
conine adresele a cte 128 blocuri, de cte 512 octei, cu informaii ale fiierului.
In figura de mai sus am ilustrat prin cercuri blocurile de informaie care aparin
fiierului, iar prin dreptunghiuri blocurile de referine, n interiorul acestora marcnd
referinele. Numrul de accese necesare pentru a obine direct un octet oarecare este cel mult
4. Pentru fiiere mici acest numr este i mai mic. Att timp ct fiierul este deschis,
inodul lui lui este prezent n memoria intern. Tabelul urmtor d numrul maxim de accese
la disc pentru a obine, n acces direct orice octet dintr-un fiier, n funcie de lungimea
fiierului.
62
La sistemele Unix actuale lungimea unui bloc este de 4096 octei care poate nregistra 1024
adrese, iar n inod se nregistreaz direct adresele primelor 12 blocuri. In aceste
condiii, tabelul de mai sus se transform n:
3.1.2.
In cadrul unui sistem de fiiere, apelurile sistem Unix gestioneaz opt tipuri de fiiere i
anume:
1. Normale (obinuite)
2. Directori
3. Legturi hard (hard links)
4. Legturi simbolice (symbolic links)
5. Socketuri (sockets)
6. FIFO - pipe cu nume (named pipes)
7. Periferice caracter
8. Periferice bloc
Pe lng aceste opt tipuri, mai exist nc patru entiti, pe care apelurile sistem le vd, din
punct de vedere sintactic, tot ca i fiiere. Aceste entiti sunt gestionate de nucleul Unix, au
suportul fizic tot n nucleu i folosite la comunicri ntre procese. Aceste entiti sunt:
9. Pipe (anonymous pipes)
10. Segmente de memorie partajat
11. Cozi de mesaje
12. Semafoare
Fiierele obinuite sunt privite ca iruri de octei, accesul la un octet putndu-se face fie
secvenial, fie direct prin numrul de ordine al octetului.
63
Fiierele directori. Un fiier director se deosebete de un fiier obinuit numai prin informaia
coninut n el. Un director conine lista de nume i adrese pentru fiierele subordonate lui.
Uzual, fiecare utilizator are un director propriu care puncteaz la fiierele lui obinuite, sau la
ali subdirectori definii de el.
Fiierele speciale. In aceast categorie putem include, pentru moment, ultimele 6 tipuri de
fiiere. In particular, Unix privete fiecare dispozitiv de I/O ca i un fiier de tip special. Din
punct de vedere al utilizatorului, nu exist nici o deosebire ntre lucrul cu un fiier disc
normal i lucrul cu un fiier special.
Fiecare director are dou intrri cu nume speciale i anume:
"." (punct) denumete generic (puncteaz spre) nsui directorul respectiv;
".." (dou puncte succesive), denumete generic (puncteaz spre) directorul printe.
Fiecare sistem de fiiere conine un director principal numit root sau /.
In mod obinuit, fiecare utilizator folosete un director curent, ataat utilizatorului la intrarea
n sistem. Utilizatorul poate s-i schimbe acest director (cd), poate crea un nou director
subordonat celui curent, (mkdir), s tearg un director (rmdir), s afieze calea de acces
de la root la un director sau fiier (pwd) etc.
Apariia unui mare numr de distribuitori de Unix a condus, inevitabil, la proliferarea unui
numr oarecare de "sisteme de fiiere extinse" proprii acestor distribuitori. De exemplu:
Solaris utilizeaz sistemul de fiiere ufs;
Linux utilizeaz cu precdere sistemul de fiiere ext2 i mai nou, ext3;
IRIX utilizeaz xfs
etc.
Actualele distribuii de Unix permit utilizatea unor sisteme de fiiere proprii altor sisteme de
operare. Printre cele mai importante amintim:
Sistemele FAT i FAT32 de sub MS-DOS i Windows 9x;
Sistemul NTFS propriu Windows NT i 2000.
Din fericire, aceste extinderi sunt transparente pentru utilizatorii obinuii. Totui, se
recomand pruden atunci cnd se efectueaz altfel de operaii dect citirea din fiierele
create sub alte sisteme de operare dect sistemul curent. De exemplu, modificarea sub Unix a
unui octet ntr-un fiier de tip doc creat cu Word sub Windows poate uor s compromit
fiierul aa nct el s nu mai poat fi exploatat sub Windows!
Administratorii sistemelor Unix trebuie s in cont de sistemele de fiiere pe care le
instaleaz i de drepturile pe care le confer acestora vis-a-vis de userii obinuii.
Principiul structurii arborescente de fiiere este acela c orice fiier sau director are un singur
printe. Automat, pentru fiecare director sau fiier exist o singur cale (path) de la rdcin
la directorul curent. Legtura ntre un director sau fiier i printe o vom numi legtur
natural. Evident ea se creeaz odat cu crearea directorului sau fiierului respectiv.
64
3.1.2.1.
In anumite situaii este util partajarea unei poriuni a structurii de fiiere ntre mai muli
utilizatori. De exemplu, o baz de date dintr-o parte a structurii de fiiere trebuie s fie
accesibil mai multor utilizatori. Unix permite o astfel de partajare prin intermediul
legturilor suplimentare. O legtur suplimentar permite referirea la un fiier pe alte ci
dect pe cea natural. Legturile suplimentare sunt de dou feluri: legturi hard i legturi
simbolice (soft).
Legturile hard sunt identice cu legturile naturale i ele pot fi create numai de ctre
administratorul sistemului. O astfel de legtur este o intrare ntr-un director care puncteaz
spre o substructur din sistemul de fiiere spre care puncteaz deja legtura lui natural. Prin
aceasta, substructura este vzut ca fiind descendent din dou directoare diferite! Deci,
printr-o astfel de legtur un fiier primete efectiv dou nume. Din aceast cauz, la
parcurgerea unei structuri arborescente, fiierele punctate prin legturi hard apar duplicate.
Fiecare duplicat apare cu numrul de legturi ctre el.
De exemplu, dac exist un fiier cu numele vechi, iar administratorul d comanda:
#ln
vechi
linknou
atunci n sistemul de fiiere se vor vedea dou fiiere identice: vechi si linknou, fiecare
dintre ele avnd marcat faptul c sunt dou legturi spre el.
Legturile hard pot fi fcute numai n interiorul aceluiai sistem de fiiere (detalii puin mai
trziu).
Legturile simbolice sunt intrri speciale ntr-un director, care puncteaz (refer) un fiier
(sau director) oarecare n structura de directori. Aceast intrare se comport ca i un
subdirector al directorului n care s-a creat intrarea.
In forma cea mai simpl, o legtur simbolic se creeaz prin comanda:
ln
- s
caleInStructuraDeDirectori
numeSimbolic
65
/:
A:
D:
E: F
A
..
..
A
-
B:
D:
..
C:
A
..
E:
F:
C:
F:
G:
G:
Structura arborescent mpreun cu legturile simbolice sau hard confer sistemului de fiiere
Unix o structur de graf aciclic. In exemplul din figura de mai sus este prezentat un exemplu
simplu de structur de fiiere. Prin literele mari A, B, C, D, E, F, G am indicat nume de
fiiere obinuite, nume de directori i nume de legturi. Este evident posibil ca acelai nume
s apar de mai multe ori n structura de directori, graie structurii de directori care elimin
ambiguitile. Fiierele obinuite sunt reprezentate prin cercuri, iar fiierele directori prin
dreptunghiuri.
Legturile sunt reprezentate prin sgei de trei tipuri:
linie continu legturile naturale;
linie ntrerupt spre propriul director i spre printe;
linie punctat legturi simbolice sau hard.
In exemplul de mai sus exist 12 noduri - fiiere obinuite sau directori. Privit ca un arbore,
deci considernd numai legturile naturale, el are 7 ramuri i 4 nivele.
S presupunem c cele dou legturi (desenate cu linie punctata) sunt simbolice. Pentru
comoditate, vom nota legtura simbolic cu ultima liter din specificarea cii. Crearea celor
dou legturi se poate face, de exemplu, prin succesiunea de comenzi:
cd /A
ln -s /A/B/D/G G
Prima legtur
cd /A/B/D
ln -s /A/E E
A doua legtur
S presupunem acum c directorul curent este B. Vom parcurge arborele n ordinea director
urmat de subordonaii lui de la stnga spre dreapta. Urmtoarele 12 linii indic toate cele 12
noduri din structur. Pe aceeai linie apar, atunci cnd este posibil, mai multe specificri ale
aceluiai nod. Specificrile care fac uz de legturi simbolice sunt subliniate. Cele mai lungi 7
ramuri vor fi marcate cu un simbol # n partea dreapt.
66
/
/A
/A/D
/A/E
/A/E/F
/A/E/G
/B
/B/D
/B/D/G
/B/E
/B/F
/C
..
../A
../A/D
../A/E
../A/E/F
../A/E/G
.
D
D/G
E
F
../C
#
D/E
D/E/F
D/E/G
./D
./D/G
./E
./F
./D/E
./D/E/F
./D/E/G
/A/G
#
#
../A/G
#
#
#
#
rm
rm
D/G
/A/G
Este clar c fiierul trebuie s rmn activ dac este ters numai de ctre una dintre
specificri.
Pentru aceasta, n descriptorul fiierului respectiv exist un cmp numit contor de legare.
Acesta are valoarea 1 la crearea fiierului i crete cu 1 la fiecare nou legtur. La tergere,
se radiaz legtura din directorul printe care a cerut tergerea, iar contorul de legare scade cu
1. Abia dac acest contor a ajuns la zero, fiierul va fi efectiv ters de pe disc i blocurile
ocupate de el vor fi eliberate.
3.2.1.
In seciunile din acest subcapitol vom prezenta cele mai importante apeluri sistem pentru
lucrul cu procese: fork, exit, wait i exec*. Incepem cu fork(), apelul sistem
pentru crearea unui proces.
3.2.1.1.
In sistemul de operare Unix un proces se creeaz prin apelul funciei sistem fork(). La o
funcionare normal, efectul acesteia este urmtorul: se copiaz imaginea procesului ntr-o
zon de memorie liber, aceast copie fiind noul proces creat, n prima faz identic cu
procesul iniial. Cele dou procese i continu execuia n paralel cu instruciunea care
urmeaz apelului fork.
67
Procesul nou creat poart numele de proces fiu, iar procesul care a apelat funcia fork() se
numete proces printe. Exceptnd faptul c au spaii de adrese diferite, procesul fiu difer de
procesul printe doar prin identificatorul su de proces PID, prin identificatorul procesului
printe PPID i prin valoarea returnat de apelul fork(). La derulare normal, un apel
fork() ntoarce n procesul printe (procesul care a lansat apelul fork()) pid-ul noului
proces fiu, iar n procesul fiu, ntoarce valoarea 0.
Inainte de fork:
Dupa fork:
Proces parinte
Proces parinte
Context
fork()
Context
fork()
Proces fiu
0
Context
fork()
if ( fork() == 0 )
{ - - - instruciuni ale procesului fiu - - - }
else
{ - - - instruciuni ale procesului tat - - - }
Programul urmtor ilustreaz utilizarea lui fork:
main(){
int pid,i;
printf("\nInceputul programului:\n");
if ((pid=fork())<0) err_sys("Nu pot face fork()\n");
else if (pid==0){//Suntem in fiu
for (i=1;i<=10;i++){
68
sleep(2);
//dormim 2 secunde
printf("\tFIUL(%d) al PARINTELUI(%d):3*%d=%d\n",
getpid(),getppid(),i,3*i);
}
printf("Sfarsit FIU\n");
}
else if (pid>0){//Suntem in parinte
printf("Am creat FIUL(%d)\n",pid);
for (i=1;i<=10;i++){
sleep(1);
//dormim 1 secunda
printf("PARINTELE(%d):2*%d=%d\n",getpid(),i,2*i);
}
printf("Sfarsit PARINTE\n");
}
}
In mod intenionat, am fcut astfel nct procesul fiu s atepte mai mult dect printele (n
cazul calculelor complexe apar adesea situaii n care operaiile unuia dintre procese dureaz
mai mult n timp). Ca urmare, printele va termina mai repede execuia. Rezultatele obinute
sunt:
Inceputul programului:
Am creat FIUL(20429)
PARINTELE(20428): 2*1=2
FIUL(20429) al PARINTELUI(20428): 3*1=3
PARINTELE(20428): 2*2=4
PARINTELE(20428): 2*3=6
FIUL(20429) al PARINTELUI(20428): 3*2=6
PARINTELE(20428): 2*4=8
PARINTELE(20428): 2*5=10
FIUL(20429) al PARINTELUI(20428): 3*3=9
PARINTELE(20428): 2*6=12
PARINTELE(20428): 2*7=14
FIUL(20429) al PARINTELUI(20428): 3*4=12
PARINTELE(20428): 2*8=16
PARINTELE(20428): 2*9=18
FIUL(20429) al PARINTELUI(20428): 3*5=15
PARINTELE(20428): 2*10=20
Sfarsit PARINTE
FIUL(20429) al PARINTELUI(1): 3*6=18
FIUL(20429) al PARINTELUI(1): 3*7=21
FIUL(20429) al PARINTELUI(1): 3*8=24
FIUL(20429) al PARINTELUI(1): 3*9=27
FIUL(20429) al PARINTELUI(1): 3*10=30
Sfarsit FIU
3.2.1.2.
Aproape toate sistemele de operare i toate mediile de programare ofer, ntr-un fel sau altul,
mecanisme de lansare a unui program din interiorul altuia. Unix ofer acest mecanism prin
intermediul familiei de apeluri sistem exec*. Dup cum se va vedea, utilizarea combinat de
fork exec ofer o mare elasticitate manevrrii proceselor.
Apelurile sistem din familia exec lanseaz un nou program n cadrul aceluiai proces.
Apelului exec i se furnizeaz numele unui fiier executabil, iar coninutul acestuia se
suprapune peste programul procesului existent, aa cum se vede n fig. 5.10.
69
Inainte de exec:
Dupa exec:
Context
vechi
Context
vechi
Context
nou
exec()
ou
mn
gra
pro
70
3.2.1.3.
Apelul sistem:
exit(int n)
provoac terminarea procesului curent i revenirea la procesul printe (cel care l-a creat prin
fork). Intregul n precizeaz codul de retur cu care se termin procesul. In cazul n care
procesul printe nu mai exist, procesul este trecul n starea zombie i este subordonat
automat procesului special init (care are PID-ul 1).
Ateptarea terminrii unui proces se realizeaz folosind unul dintre apelurile sistem wait()
sau waitpid(). Prototipurile acestora sunt:
pid_t wait(int *stare)
pid_t waitpid(pid_t pid, int *stare, int optiuni);
Apelul wait() suspend execuia programului pn la terminarea unui proces fiu. Dac fiul
s-a terminat nainte de apelul wait(), apelul se termin imediat. La terminare, toate
resursele ocupate de procesul fiu sunt eliberate.
3.2.2.
3.2.2.1.
Conceptul de pipe
Conceptul a aprut prima dat sub Unix, pentru a permite unui proces fiu s comunice cu
printele su. De obicei procesul printe redirecteaz ieirea sa standard, stdout, ctre un
pipe, iar procesul fiu i redirecteaz intrarea standard, stdin, din acelai pipe. In
majoritatea sistemelor de operare se folosete operatorul | pentru specificarea acestui gen
de conexiuni ntre comenzi ale sistemului de operare.
Un pipe Unix este un flux unidirecional de date, gestionat de ctre nucleul sistemului. De
fapt, n nucleu se rezerv un buffer de minimum 4096 octei n care octeii sunt gestionai aa
cum am descris mai sus. Crearea unui pipe se face prin apelul sistem:
int pipe(int fd[2]);
Intregul fd[0] se comport ca un ntreg care identific descriptorul pentru citirea din
"fiierul" pipe, iar fd[1] ca un ntreg care indic descriptorul pentru scriere n pipe. In urma
crerii, legtura user nucleu prin acest pipe apare ca n figura de mai jos.
71
read fd[0]
proces user:
write fd[1]
pipe
nucleu:
sensul datelor
Evident, un pipe ntr-un singur proces nu are sens. Este ns esenial funcionarea lui pipe
combinat cu fork. Astfel, dac dup crearea lui pipe se execut un fork, atunci legtura
celor dou procese cu pipe din nucleu apare ca n figura urmtoare.
Procesul parinte
Procesul fiu
read
read
fd[0]
write
fd[1]
fd[0]
fd[1]
write
Nucleu
pipe
Sensul datelor
Figura 3.3 Un pipe leag dou procese nrudite
Asigurarea unidirecionalitii unui pipe cade exclusiv n sarcina programatorului. Astfel,
pentru a se asigura sensul datelor n exemplul de mai sus, se impune ca nainte de a transmite
prin pipe:
In procesul printe s se apeleze close(fd[0]);
In procesul fiu s se apeleze close(fd[1]);
Natural, dac se dorete ordinea invers, atunci se vor executa operaiile close(fd[1]) n
procesul printe i close(fd[0]) n procesul fiu.
3.2.2.2.
72
$ who | sort
Noi vom prezenta realizarea conexiunii ntre cele dou comenzi: who | sort prin pipe.
Procesul printe (care nlocuiete procesul shell) genereaz doi fii, iar acetia i redirecteaz
corespunztor intrrile / ieirile. Primul dintre ele execut who, cellalt sort, iar printele
ateapt terminarea lor. Sursa este prezentat n programul de mai jos.
//whoSort.c
//Lanseaza in pipe comenzile shell: $ who | sort
#include <unistd.h>
main (){
int p[2];
pipe (p);
if (fork () == 0) {
// Primul fiu
dup2 (p[1], 1);
//redirectarea iesirii standard
close (p[0]);
execlp ("who", "who", 0);
}
else if (fork () == 0) {
// Al doilea fiu
dup2 (p[0], 0);
// redirectarea intrarii standard
close (p[1]);
execlp ("sort", "sort", 0);//executie sort
}
else {
// Parinte
close (p[0]);
close (p[1]);
wait (0);
wait (0);
}
}
Observatie: pentru a se nelege mai bine exemplul de mai sus, invitm cititorul s citeasc
din manualele Unix prezentarea apelului sistem dup2. Aici apelul dup2 are ca paramentru
un descriptor pipe.
3.2.3.
3.2.3.1.
Conceptul de FIFO
Cel mai mare dezavantaj al lui pipe sub Unix este faptul c poate fi utilizat numai n
procese nrudite: procesele care comunic prin pipe trebuie s fie descendeni din procesul
creator al lui pipe. Aceasta deoarece ntregii descriptori de citire/scriere din/n pipe sunt unici
i sunt transmii proceselor fiu ca urmare a apelului fork().
In jurul anului 1985 (Unix System V), a aprut conceptul FIFO ( pipe cu nume). Acesta este
un flux de date unidirecional, accesat prin intermediul unui fiier rezident n sistemul de
fiiere. Incepnd cu Unix System V, exist fiiere de tip FIFO. Spre deosebire de pipe,
fiierele FIFO au nume i ocup un loc n sistemul de fiiere. Din aceast cauz, un FIFO
73
poate fi accesat de orice dou procese, nu neaprat cu printe comun. Atenie ns: chiar dac
un FIFO exist ca fiier n sistemul de fiiere, pe disc nu se stocheaz nici o dat care trece
prin canalul FIFO, acestea fiind stocate i gestionate n buffer-ele nucleului sistemului de
operare!
Conceptual, canalele pipe i FIFO sunt similare. Deosebirile eseniale dintre ele sunt
urmtoarele dou:
suportul pentru pipe este o poriune din memoria RAM gestionat de nucleu, n timp
ce FIFO are ca suport discul magnetic;
toate procesele care comunic prin-un pipe trebuie s fie descendente ale procesului
creator al canalului pipe, n timp ce pentru FIFO nu se cere nici o relaie ntre
procesele protagoniste.
Crearea unui fifo se poate face folosind unul dintre apelurile:
int mknod (char *numeFIFO, int mod, 0);
int mkfifo (char *numeFIFO, int mod);
Menionm c cele dou apeluri de mai sus, dei sunt specificate de POSIX, nu sunt
amndou apeluri sistem pe toate implementrile de Unix. Astfel, pe FreeBSD sunt prezente
ambele apeluri sistem mknod() i mkfifo(), dar pe Linux i Solaris exist numai apelul
sistem mknod(), funcia de bibliotec mkfifo() fiind implementat cu ajutorul apelului
sistem mknod(). Cele dou comenzi shell sunt ns disponibile pe majoritatea
implementrilor de Unix. Sub sistemele Unix mai vechi, comenzile mknod i mkfifo sunt
permise numai super-user-ului. Incepnd cu Unix System V 4.3 ele sunt disponibile i
utilizatorului obinuit.
tergerea (distrugerea) unui FIFO se poate face fie cu comanda shell rm numeFIFO, fie cu
un apel sistem C unlink() care cere un descriptor pentru fiierul FIFO.
Odat ce FIFO este creat, el trebuie s fie deschis pentru citire sau scriere folosind apelul
sistem open. Precizarea sau nu a flagului O_NDELAY la apelul sistem open are efectele
indicate n tabelul urmtor.
74
Condiii
deschide
FIFO
read-only, dar nu
exist proces de
scriere n FIFO
deschide
FIFO
write-only, dar nu
exist proces de
citire din FIFO
citire din FIFO sau
din pipe, dar nu
exist date de citit
normal
setat O_NDELAY
ateapt pn cnd apare un proces care revine imediat fr a
semnala eroare
deschide FIFO pentru scriere
revine
imediat
cu
semnalarea de eroare:
variabila errno va
deveni ENXIO
ateapt pn cnd apar date n pipe sau revine
imediat,
cu
FIFO, sau pn cnd nu mai exist proces ntoarcerea valorii 0
deschis pentru scriere. Intoarce numrul de
date citite dac apar noi date, sau 0 dac nu
mai exist proces de scriere
imediat,
cu
scrie n FIFO sau ateapt pn cnd se face spaiu disponibil, revine
pipe, dar acesta apoi scrie attea date, ct i permite spaiul ntoarcerea valorii 0
disponibil
este plin
Aceste reguli trebuie completate cu regulile de citire/scriere de la nceputul capitolului despre
comunicaii prin fluxuri de octei. De asemenea, nainte de a fi folosit, un canal FIFO trebuie
s fie n prealabil deschis pentru citire de un proces i deschis pentru scriere de alt proces.
3.2.3.2.
Modelul de aplicaie client / server este clasic n programare. In cele ce urmeaz vom ilustra
o schem de aplicaii client / server bazate pe comunicaii prin FIFO. Pentru a se asigura
comunicarea bidirecional se folosesc dou FIFO-uri. Pentru partea specific aplicaiei, se
folosesc metodele client(int in, int out) i server(int in, int out).
Fiecare dintre ele primete ca i parametri descriptorii de fiiere, presupuse deschise, prin
care comunic cu partenerul.
In cele dou programe care urmeaz este schiat schema serverului i a clientului. Cele dou
programe presupun c cele dou canale FIFO sunt create, respectiv terse, prin comenzi Unix.
Programul server:
#include
#include
#include
#include
#include
<sys/types.h>
<sys/stat.h>
<sys/errno.h>
<stdio.h>
<unistd.h>
#include "server.c"
#define FIFO1 "/tmp/fifo.1"
#define FIFO2 "/tmp/fifo.2"
main() {
int
readfd, writefd;
75
}
Programul client:
#include
#include
#include
#include
#include
<sys/types.h>
<sys/stat.h>
<sys/errno.h>
<stdio.h>
<unistd.h>
#include "client.c"
extern int errno;
#define FIFO1 "/tmp/fifo.1"
#define FIFO2 "/tmp/fifo.2"
main() {
int
readfd, writefd;
76
3.3.1.
Un interpretor de comenzi (shell) este un program special care furnizeaz o interfa ntre
nucleul sistemului de operare Unix (kernel-ul) i utilizator. Din aceast perspectiv, a
asigurrii legturii ntre utilizator i sistem, un shell poate fi privit diferit:
1. limbaj de comand care asigur interfaa dintre calculator i utilizator. In momentul n
care un utilizator i deschide o sesiune de lucru, n mod implicit, un shell se
instaleaz ca interpretor de comenzi. Shell-ul afieaz la ieirea standard (asociat de
obicei unui terminal) un prompter, invitnd astfel utilizatorul s introduc comenzi
sau s lanseze n execuie fiiere de comenzi, eventual parametrizate.
2. limbaj de programare, ce are ca element de baz (element primitiv) comanda Unix
(similar semantic cu instruciunea de atribuire din limbajele de programare). Ca i
element primitiv de dirijare a succesiunii elementelor de baz este valoarea codului
de retur al ultimei comenzi executate: valoarea 0 nseamn true, valoare nenul
nseamn false (corespondentul din limbajele de programare clasice este condiia).
Shell-urile dispun de conceptele de variabil, constant, expresie, structuri de control
i subprogram. Spre deosebire de alte limbaje de programare, expresiile cu care
lucreaz shell-urile sunt preponderent iruri de caractere. In ceea ce privete cerinele
sintactice, acestea au fost reduse la minim prin eliminarea parantezelor de delimitare a
parametrilor, a diferitelor caractere de separare i terminare, a declaraiilor de
variabile, etc.
un shell lansat n execuie la deschiderea unei sesiuni de lucru va rmne activ pn la
nchiderea respectivei sesiuni. Odat instalat, acesta lucreaz conform algoritmului urmtor:
CtTimp (nu s-a nchis sesiunea)
Afieaz prompter;
Citete linia de comand;
Dac ( linia se termin cu '&' ) atunci
Creaz un proces i-i d spre execuie comanda
Nu ateapt ca execuia s se termine
Altfel
Creaz un proces i-i d spre execuie comanda
Ateapt s se termine execuia comenzii
SfDac
SfCtTimp
Este important s remarcm, din algoritmul de mai sus, cele dou moduri n care o comand
poate fi executat:
modul foreground - execuie la vedere. In acest gen de execuie sh lanseaz execuia
comenzii, ateapt terminarea ei dup care afieaz din nou prompterul pentru o nou
comand. Acesta este modul implicit de execuie al oricrei comenzi Unix.
modul background - execuie n fundal, ascuns. In acest gen de execuie sh lanseaz
procesul care va executa comanda, dar nu mai ateapt terminarea ei ci afieaz
imediat prompterul, oferind utilizatorului posibilitatea de a lansa imediat o nou
77
3.3.2.
3.3.2.1.
Programarea n shell
In cele ce urmeaz vom prezenta gramatica limbajului sh cel mai simplu shell de sub Unix.
Vom pune n eviden principalele categorii sintactice, semantica / funcionalitatea fiecrei
astfel de categorii se deduce uor din context.
Vom considera urmtoarele convenii, pe care le folosim doar n scrierea regulilor gramaticii:
O categorie gramatical se poate defini prin una sau mai multe alternative de definire.
Alternativele se scriu cte una pe linie, ncepnd cu linia de dup numele categoriei
gramaticale, astfel:
categorieGramatical:
alternativa_1 de definire
---alternativa_n de definire
[ ]? Semnific faptul c, construcia dintre paranteze va aprea cel mult odat.
[ ]+ Semnific faptul c, construcia dintre paranteze va aprea cel puin odat.
[ ]* Semnific faptul c, construcia dintre paranteze poate s apar de 0 sau mai multe
ori.
Folosind aceste convenii, sintaxa limbajului sh (n partea ei superioar, fr detalii) este
descris n fig. 2.2.
Semnificaia unora dintre elementele sintactice din fig. 2.2 este:
cuvnt: secven de caractere diferite de caracterele albe (spaiu, tab)
nume: secven ce ncepe cu liter i continu cu litere, cifre, _ (underscore)
cifra: cele 10 cifre zecimale
O comanda sh poate avea oricare dintre cele 9 forme prezentate. Una dintre modalitile de
definire este cea de comandElementar, unde o astfel de comand elementar este un ir de
elemente, un element putnd fi definit n 10 moduri distincte. O legarePipe este fie o singur
comand, fie un ir de comenzi separate prin caracterul special '|'. In sfrit, listaCom este o
succesiune de legarePipe separate i eventual terminate cu simboluri speciale.
78
Structurile alternative if i case sunt nchise de construciile fi, respectiv esac, obinute prin
oglindirea cuvintelor de start. In cazul ciclurilor repetitive, sfritul acestora este indicat prin
folosirea cuvntului rezervat done. Nu s-a folosit construcia similar corespunztoare lui do,
deoarece od este numele unui comenzi clasice Unix.
Incheiem acest subcapitol cu prezentarea sintaxei unor construcii rezervate, precum i a
unor caractere cu semnificaie special n shell-ul sh.
a) Construcii sintactice:
|
legare pipe
&&
legare andTrue
||
legare orFalse
;
separator / terminator comand
;;
delimitator case
(), {}
grupri de comenzi
< <<
redirectri intrare
> >>
redirectri ieire
&cifra, &specific intrare sau ieire standard
b) Machete i specificri generice:
*
nlocuiete orice ir de caractere
?
nlocuiete orice caracter
[...]
nlocuiete cu orice caracter din ...
Observaie: aceste machete i specificri generice nu trebuie confundate cu convenia
propus la nceputul subcapitolului pentru scrierea gramaticii limbajului sh
comand:
comandElementar
( listaCom )
{ listaCom }
if listaCom then listaCom [ elif listaCom then listaCom ]* [ else listaCom ]? fi
case cuvant in [ cuvant [ | cuvant ]* ) listaCom ;; ]+ esac
for nume do listaCom done
for nume in [ cuvant ]+ do listaCom done
while listaCom do listaCom done
until listaCom do listaCom done
comandElementar:
[ element ]+
79
listaCom:
legarePipe [ separator legarePipe ]* [ terminator ]?
legarePipe:
comanda [ | comanda ]*
element:
cuvnt
nume=cuvnt
>cuvnt
<cuvnt
>>cuvnt
<<cuvnt
>&cifra
<&cifra
<&>&separator:
&&
||
terminator
terminator:
;
&
a. Descriei pe scurt funcionarea apelului sistem fork i valorile pe care le poate returna.
b. Ce tiprete pe ecran secvena de program de mai jos, considernd c apelul sistem
fork se execut cu succes? Justificai rspunsul.
int main() {
int n = 1;
if(fork() == 0) {
n = n + 1;
exit(0);
}
n = n + 2;
printf(%d: %d\n, getpid(), n);
wait(0);
return 0;
}
80
for F in *.txt; do
K=`grep abc $F`
3
4
5
6
if [ $K != ]; then
echo $F
fi
done
II.
a. Se d fragmentul de cod de mai jos. Indicai liniile care se vor tipri pe ecran n ordinea n
care vor aprea, considernd c apelul sistem fork se execut cu succes? Justificai
rspunsul.
int main() {
int i;
for(i=0; i<2; i++) {
printf("%d: %d\n", getpid(), i);
if(fork() == 0) {
printf("%d: %d\n", getpid(), i);
exit(0);
}
}
for(i=0; i<2; i++) {
wait(0);
}
return 0;
}
81
4. Bibliografie general
1. ***: Linux man magyarul, http://people.inf.elte.hu/csa/MAN/HTML/index.htm
2. A.S. Tanenbaum, A.S. Woodhull, Opercis rendszerek, 2007, Panem Kiad.
3. Alexandrescu, Programarea modern in C++. Programare generic si modele de
proiectare aplicate, Editura Teora, 2002.
4. Angster Erzsbet: Objektumorientlt tervezs s programozs Java, 4KR Bt, 2003.
5. Bartk Nagy Jnos, Laufer Judit, UNIX felhasznli ismeretek, Openinfo
6. Bjarne Stroustrup: A C++ programozsi nyelv, Kiskapu kiad, Budapest, 2001.
7. Bjarne Stroustrup: The C++ Programming Language Special Edition, AT&T, 2000.
8. Boian F.M. Frentiu M., Lazr I. Tambulea L. Informatica de baz. Presa Universitar
Clujeana, Cluj, 2005
9. Boian F.M., Ferdean C.M., Boian R.F., Drago R.C., Programare concurent pe
platforme Unix, Windows, Java, Ed. Albastr, Cluj-Napoca, 2002
10. Boian F.M., Vancea A., Bufnea D., Boian R.,F., Cobrzan C., Sterca A., Cojocar D.,
Sisteme de operare, RISOPRINT, 2006
11. Bradley L. Jones: C# mesteri szinten 21 nap alatt, Kiskapu kiad, Budapest, 2004.
12. Bradley L. Jones: SAMS Teach Yourself the C# Language in 21 Days, Pearson
Education,2004.
13. Cormen, T., Leiserson, C., Rivest, R., Introducere n algoritmi, Editura Computer
Libris Agora, Cluj, 2000
14. DATE, C.J., An Introduction to Database Systems (8th Edition), Addison-Wesley,
2004.
15. Eckel B., Thinking in C++, vol I-II, http://www.mindview.net
16. Ellis M.A., Stroustrup B., The annotated C++ Reference Manual, Addison-Wesley,
1995
17. Frentiu M., Lazr I. Bazele programrii. Partea I-a: Proiectarea algoritmilor
18. Herbert Schildt: Java. The Complete Reference, Eighth Edition, McGraw-Hill, 2011.
19. Horowitz, E., Fundamentals of Data Structures in C++, Computer Science Press,
1995
20. J. D. Ullman, J. Widom: Adatbzisrendszerek - Alapvets, Panem kiado, 2008.
21. ULLMAN, J., WIDOM, J., A First Course in Database Systems (3rd Edition),
Addison-Wesley + Prentice-Hall, 2011.
22. Kiad Kft, 1998, http://www.szabilinux.hu/ufi/main.htm
23. Niculescu,V., Czibula, G., Structuri fundamentale de date i algoritmi. O perspectiv
orientat obiect., Ed. Casa Crii de Stiin, Cluj-Napoca, 2011
24. RAMAKRISHNAN, R., Database Management Systems. McGraw-Hill, 2007,
http://pages.cs.wisc.edu/~dbbook/openAccess/thirdEdition/slides/slides3ed.html
25. Robert Sedgewick: Algorithms, Addison-Wesley, 1984
26. Simon Kroly: Kenyernk Java. A Java programozs alapjai, Presa Universitar
Clujean, 2010.
27. Tmbulea L., Baze de date, Facultatea de matematic i Informatic, Centrul de
Formare Continu i Invmnt la Distan, Cluj-Napoca, 2003
28. V. Varga: Adatbzisrendszerek (A relcis modelltl az XML adatokig), Editura Presa
Universitar Clujean, 2005, p. 260. ISBN 973-610-372-2
82