|
Regulile de sintaxa par complicate la prima vedere,dar sunt in esenta
|
səhifə | 10/55 | tarix | 07.05.2018 | ölçüsü | 4,6 Mb. | | #50260 |
| Regulile de sintaxa par complicate la prima vedere,dar sunt in esenta
lor destul de simple si intuitive.Cu putina practica,devin chiar usoare.
Sunt indispensabile atunci cand definiti clase de obiecte.Pentru inceput,
este bine sa utilizati clase de obiecte cat mai simple si clare,cu nu
mai mult de doua sau trei clase derivate si/sau intricate,in cate puteti
sa observati din ochi calea de acces la fiecare membru al claselor.Pentru
structuri mai complicate,este bine sa desenati pe hartie cu pataratele
arhitectura datelor structurate declarate si sa urmariti caile de acces
la fiecare dintre membri,astfel incat programul sa fie cat mai usor de
declarat.
Versiunile mai noi ale limbajului C++(de exemplu Visual C++ 6.0) au
simplificat foarte mult aceasta problema,prin adaugarea unui numar foarte
mare de clase de obiecte predefinite,inclusa intr-o biblioteca de obiecte
de 1 Gb care contine clase de obiecte predefinite pentru aproape toate
tipurile posibile de aplicatii.In plus,clasele MFC sunt si ordonate ierar-
hic,astfel incat si regulile si clauzele de acces sunt gata rezolvate si
nu mai necesita nici un efort din partea programatorului.Biblioteca MFC,
contine circa 250 de astfel de clase de obiecte predefinite,structurate
dupa tipul de aplicatie(arhitectura,grafica,ferestre,comenzi,meniuri etc.)
Cu ajutorul acestei biblioteci,programatorul alege doar din arborele ie-
rarhic clasa cea mai potrivita si o introduce in aplicatia sa.
Programul mai include de altfel inca doua biblioteci,respectiv ATL
(adica Active Template Library) si OLE DB (adica Object Linked Embadded
Data Bases) care contin clase predefinite parametrizate si exemple de
obiecte generate cu clase C++.
Pentru o intelegere cat mai intuitiva a modului de editare si respectiv
a modului de executie a unui program in limabj C++,cea mai simpla compa-
ratie este cea cu jocul de domino(principiul domino-ului) sau cu jocul de
puzzle sau scrabble.Cei pasionati ai acestor jocuri,vor aprecia mult mai
usor fatetele limbajului C++.Inconvenientul consta in faptul ca majorita-
tea aplicatiilor vor functiona dupa principiul "totul sau nimic",fapt ce
determina nenumarate complicatii in munca de depanare a programelor.
-65-
CONVERSII ALE TIPURILOR DE DATE
Obiectele din clase diferite pot fi convertite la alt tip de date.In
acest sens sa va construi un obiect din tipul final de date,folosind ca
sursa obiectul initial.Procesul se numeste conversie prin constructor,
deoarece utilizeaza constructorul obiectului initial si apoi schimba
doar specificatorul de tip.
Conversiile intre date din tipurile fundamentale(int,char,float etc.)
se numesc conversii standard.Conversiile standard sunt:-promotia integrala
conversia integrala,conversia in virgula mobila,conversia integrala si in
virgula mobila,conversia aritmetica,conversia pointerilor,conversia re-
ferintei,conversia din pointer in membru.
Promotia integrala-obiectele de tip int pot fi convertite in alte
obiecte de tip int dar mai mari(ocupa mai multi bytes/data).Se poate uti-
liza pentru obiecte,siruri de caractere si constante de tip char si short
int,enumarari,campuri de biti de tip int sau enumeratori.
Conversia integrala-are loc intre date de tip integral(char,int,long)
si versiunile lor(short,signed,unsigned).
In cazul conversiei din signed in unsigned,in urma conversiei se pastreaza
valoarea absoluta,dar se poate pierde semnul minus astfel incat evaluarea
expresiilor va determina rezultate diferite.Exemplu : daca short i=-3 este
convertit la unsigned short i acesta va deveni 3.
In cazul conversiei din unsigned in signed,pot apare erori atunci cand
valorile unsigned sunt mai mari decat domeniul de valori signed.(Exemplu
unsigned short y=65000 este in afara domeniului fata de signed short y).
Atunci cand datele de tip intreg sunt convertite in alte date de tip
intreg dar mai mici(ocupa un spatiu mai mic si au domeniu de valori mai
restrans),procesul poarta numele de conversie standard si poate avea ca
rezultat pierderea de date,atunci cand valoarea convertita este mai mare
decat domaniul de valori al tipului final de date.
Conversia in virgula mobila-se refera la conversia datelor de tip
float,in date de tip float cu precizie mai mare:din float in double sau
din double in long double.In acest caz conversia este sigura si se pas-
treaza valorile.In cazul conversiei datelor cu precizie mare in date cu
precizie mai mica(Exemplu din long double in float),conversia este sigura
doar daca valoarea se afla in domeniul de reprezentare al datei finale,iar
in caz contrar va rezulta o data nedefinita(data infinita).
EXEMPLU: cout <<(float)1E300<
poate reprezenta doar numere pana la 1E+38.
Conversia dintre datele integrale si cele in virgula mobila decurge
astfel: -conversia datelor float in int se face prin trunchierea partii
fractionale fara rotunjire(Exemplu 3.14 devine 3)
-conversia datelor int in float este de obicei facila.Daca valoa-
rea originala nu poate fi reprezentata exact,atunci se va utiliza valoarea
imediat apropiata(mai mica sau mai mare) dintre cele reprezentabile.
Conversia aritmetica-daca intr-o expresie apar doi operanzi de tipuri
diferite,atunci cel de tip mai mic va fi convertit la tipul mai mare de
date,astfel incat datele sa fie representabile(Exemple: din int in float,
din float in double sau long double etc.).Procesul prin care operatorii
aritmetici binari convertesc datele pentru a fi reprezentabile,se numeste
conversie aritmetica uzuala.
-66-
Pointerii pot fi convertiti in timpul operatiilor de atribuire,initiali-
zare,comparatie etc.
-o constanta care returneaza zero sau o expresie de tip void poate fi
convertita la untip de pointer denumit "pointer null" care se va com-
porta diferit de orice tip de pointer orientat spre o functie sau un
obiect valid.
-pointerii spre tipul void pot fi convertiti in oricare alt tip de
pointeri.Un pointer spre un obiect incomplet(definit dar fara valoare,
sau fara clasa de baza definita) poate fi convertit in pointer spre
tipul void si viceversa.
-un pointer spre orice obiect care nu este "const" sau "volatile" poate
fi convertit in pointer de tipul void
-un pointer spre orice functie poate fi convertit la tipul void daca
tipul void* poate include acel pointer(daca spatiul de memorie este
suficient).
-un pointer spre o clasa,poate fi convertit in pointer spre clasa de
baza in doua situatii:-1.daca clasa de baza este accesibila (este de-
clarata "public") si conversia nu este ambigua si 2.daca se precizeaza
explicit conversia dorita(operatie de atribuire explicita)
-o expresie de tip arie poate fi convertita la un pointer.Rezultatul
este un pointer spre elementul zero(primul) al ariei.
EXEMPLU: char szPath[_MAX_PATH}; //arie de tip caracter
char *pszPath=szPath // este egala cu &szPath[0]
-pointerii spre membrii unor structuri de date(struct,class) se com-
porta diferit de pointerii obisnuiti si nu respecta conventiile stan-
dard de converise de mai sus(cu exceptia pointerilor spre membrii de
tip static).Pointerii spre membrii se realizeaza cu operatorii ::*,
.* si ->* se refera al funciile si membrii structurii respective.
Nu exista conventii de conversie pentru datele declarate de tip const
sau volatile,dar in situatiile care se preteaza la astfel de conversii
acestea pot fi specificate explicit.
Conversia pentru referinte:-o referinta la o anumita clasa poate fi con-
vertita la o referinta spre clasa de baza,cu conditia ca clasa de baza
sa fie accesibila(declarata "public") iar converisa sa nu fie ambigua
(sa fie precizate explicit denumiri distincte si unice).
Conversia pointerilor catre membrii claselor se poate face in urmatoarele
situatii:
-conversia unui membru care este o constanta cu valoarea zero se poate
face spre un "pointer null".
-conversia unui pointer spre un membru al clasei de baza catre un
pointer spre un membru al unei clase derivate din clasa de baza se
poate face daca este posibila si conversia inversa(de la clasa derivata
spre clasa de baza) adica ambele clase sunt accesibile si daca clasa
derivata nu a mostenit de la clasa de baza un membru virtual (daca nu
este incomplet definita).
In afara conversiilor standard,descrise mai sus,tipurile de date pot fi
convertite si prin definitii explicite precizate de catre utilizator.
Pentru a fi valide,conversiile explicite precizate de catre utilizator
trebuie sa nu fie ambigue(sa utilizeze nume distincte si unice pentru aria
de vizibilitate respectiva.
-67-
FUNCTII
Prototipul unei functii trebuie sa stabileasca numele functiei,tipul
de date returnat de catre functie,respectiv numarul si tipul parametrilor
sai formali.
Declararea unei functii se poate face in interiorul unei alte funcii,
caz in care se numeste functie definita intern,sau locala,sau cu vizibi-
litate locala.Daca declararea unei functii se face in afara oricarei alte
structuri de date,atunci functia este globala,sau cu vizibilitate globala
sau "cu vizibilitate in toata fila program".Indiferent daca au fost decla-
rate intern sau extern,definitia functiei trebuie facuta intotdeauna ex-
tern(spre deosebire de variabile care pot fi definite si intern).
Singurii specificatori care modifica declaratia unei functii sunt
"static" si "extern" prin care se determina daca functia poate sau nu
poate fi referita din alte file ale programului."Extern" este implict.
Tipul de date returnat de functie se precizeaza prin specificatorii de
tip(void,char,int,float etc.) si poate fi oricare dintre tipurile funda-
mentale.Tipul atribui implicit este int.Rezultatul evaluarii functiei va
fi returnat in momentul in care executia ajunge la o instructiune RETURN.
Argumentele functiei sunt nume de valori se se trensmit functiei in
momentul apelarii.Parametrii sunt valori pe care functia se sconteaza ca
la va lua in timpul executiei.In prototipul unei functii,lista completa a
parametrilor este inclusa intre parantezele ce urmeaza dupa numele fun-
ctiei.In declaratie,parametrii trebuie sa specifica tipul,marimea si va-
loarea parametrilor.
Parametrii functiilor declarati cu "auto" genereaza erori.Fiecare para-
metru trebuie sa fie precedat de specificatorul sau de tip.
EXEMPLU: void new(double x,double y,double z)
{
x=y*z // expresia care defineste functia
};
Daca in lista de argumente a functiei figureaza cel putin un parametru,
atunci lista poate sa se incheie cu o virgula urmata de trei puncte(,...)
prin care se indica faptul ca functia poate avea argumente variabile.No-
tatia poarta numele de "ellipsis notation".
Corpul functiei este un bloc compact de expresii prin care se specifica
ce anume face functia respectiva si se include intre acolade.Variabilele
declarate in corpul functiei vor fi "locale",adica vor fi vizibile doar
in interiorul functiei.
In timpul executiei unui program,controlul trece peste definitia unei
functii pana cand intalneste instructiunea RETURN,moment in care evalueaza
functia si returneaza valoarea(este important atunci cand depanati un pro-
gram).
Argumentele functiei se trec intre parantezele ce urmeaza numelui,sub
forma de lista.Daca functia nu are nici un argument se va utiliza speci-
ficatorul void.
Argumentele pot fi din oricare dintre tipurile fundamentale de date,
pot fi structuri sau uniuni de date sau pointeri.Toate argumentele se
transfera functiei prin valoarea lor.
Argumentele nu pot fi arii sau functii,dar pot fi pointeri orientati
spre aceste tipuri de date.
-68-
Apelarea(solicitarea) unei functii se face prin o expresie ce include
numele functiei respective.O expresie care apeleaza o functie,va returna
o valoare de aceeasi tip cu cel al functiei apelate.Functiile nu pot
returna obiecte de tip arie(nu lucreaza cu arii).Daca o functie este de
tip void si rezultatul apelarii functiei va fi tot de tip void.
La evaluarea unei functii,daca este necesar,se fac automat si con-
versiile standard intre argumentele functiei,astfel incat rezultatul
evaluarii sa fie reprezentabil.
O functie poate fi apelata si recursiv,adica sa se apaleze pe sine.In
acest caz functia contine in corpul sau si o expresie prin care se auto
apeleaza pana cand se indeplineste o anumita conditie.Numarul de bucle de
repetitie este determinat de volumul de memorie alocat in stiva pentru
parametrii functie.La fiecare reapelare,se vor arhiva in stiva noile
valori ale parametrilor si ale variabilelor "auto" si "register".In unele
situatii,acest gen de functii ajung sa depaseasca volumul de memorie.
O functie poate avea un numar variabil de parametri,caz in care acestia
vor fi specificati utilizand notatia "ellipsis" (,...).In absenta acestei
notatii,functia nu poate primi parametri aditionali fata de cei declarati
in lista de parametrii.Se utilizeaza pentru functii care vor primi si
parametri suplimentari fata de cei declarati initial.Pentru apelarea unei
astfel de functii,se va specifica numele functiei si un numar oarecare de
argumente.
EXEMPLU: int medie(int primul,int secundul,...);
aceasta functie va putea face media dintre doi sau mai multi parametri
adaugati ulterior.
Pentru a obtine informatii despre legaturile din program se pot utiliza
functiile specializate Call Graph(prezinta toate functiile pe care le
apeleaza functia respectiva) sau Callers Graph(prezinta toate functiile
din care este apelata functia respectiva).
Supraincarcarea functiilor(function overloading) este procedeul prin
care se declara mai multe functii cu acelasi nume,in acelasi domeniu de
vizibilitate.Prin acest procedeu se pot declara functii cu semantica
diferita cu numar diferit de argumante sau cu tipuri de date diferite.
Compilatorul C++,nu numai ca permite acest procedeu dar si selecteaza
dintre definitiile functiei pe cea care este cea mai potrivita cu tipul
de date utilizat in program (operatie de altfel hazardata si nerecomanda-
bila in majoritatea situatiilor-rezultatul poate fi nescontat).
Versiunile profesionale ofera si instrumente cu care se poate efectua
un profil al functiilor din program(timpul de executie,functiile accesate,
functiile neaccesate sau atributiile functiilor accesate).
Prototipul functiilor stabileste numele functiei si tipul returnat,cat
si numarul parametrilor formali(nu include si corpul functiei).
Functiile template,sunt functii parametrizate.Se utilizeaza pentru a
defini familii de functii cu parametri standardizati.
Functiile pot fi grupate in fisiere separate,caz in care toate fun-
ctiile vor fi incarcate in memorie in bloc,sau pot fi arhivate sub forma
de bibiloteci,caz in care se vor putea incarca in memorie doar acele
functii de care este nevoie pentru executia programului.
Limabjul C contine o biblioteca standard cu peste 500 de functii,gata
implementate pentru o mare diversitate de aplicatii,functii ce trebuiesc
incarcate selectiv,inainte de incarcarea aplicatiei in care actioneaza.
-69-
PREPROCESORUL C
Toate elementele prezentate anterior sunt incluse in executabilul
care contine rutinele programului C si sunt incarcate in memoria de lucru
la fiecare pornire a programului.Elementele descrise sunt suficiente
pentru efectuarea de operatii complexe sau de analiza si sinteza a datelor
dar nu contin elemente pentru comunicarea cu utilizatorul si nici facili-
tati de afisare si prezentare grafica a datelor sau a rezultatelor.
Pentru comunicarea cu utilizatorul(afisare pe ecran,imprimare,grafica,
introducerea de date de la tastatura,mouse etc.) sunt necesare functii
auxiliare ce se vor incarca selectiv,in functie de necesitatile fiecarei
aplicatii.Aceste functii sunt arhivate in biblioteca standard a progra-
mului.Pentru a putea fi utilizate in program,trebuie sa existe in memorie.
Din acest motiv,functiile auxiliare se incarca la pornirea aplicatiei cu
ajutorul unor directive denumite "de preprocesor",deoarece sunt executate
in etapa premergatoare celei de procesare a datelor.
Astfel,programarea unei aplicatii incepe cu alegerea functiilor auxili-
are necesare si incarcarea lor in memorie cu ajutorul comenzii #include.
Preprocesorul este de fapt un procesor de text care manipuleaza fragmente
de text din fila sursa(declaratii de functii) in etapa initiala a fazei
de transcriere(compilare) a datelor din program.Preprocesorul nu face
analiza sintactica a expresiilor ci doar le separa in unitati distincte
(token) si le incarca in memorie.Pentru a obtine o lista a codului sursa
dupa preprocesare se poate utiliza optiunea de compilare /E sau /EP.
Prin preprocesare,se vor incarca in memorie,atat fila de coduri sursa
redactata de utilizator cat si filele cu date auxiliare specificate prin
comenzi #include,realizand astfel programul complet,denumit si unitate de
translatare.Din filele adaugate,se pot exclude conditional,una sau mai
multe expresii utilizand comenzi de genul #if.
Diferitele file ce contin coduri sursa pot comunica intre ele prin:
-apeluri catre functii specificate de tip "extern"
-apeluri catre membri ai functiilor declarati de tip "extern"
-modificare directa a obiectelor declarate de tip "extern"
-modificare directa a filelor sursa(inserari,deletii etc.)
-comunicatii intre procesari (numai pentru Microsoft Windows)
Etapele succesive ale procesului de compilare a programelor sunt:
1.-Character mapping-caracterele sunt reprezentate intern
2.-Line splicing-liniile de cod sunt defragmentate
3.-Tokenization-codul sursa este fragmentat in elemente (token)
4.-Preprocessing-se executa comenzile de preprocesare
5.-Character-set mapping-secventele de caractere sunt convertite in
echivalentul lor executabil
6.-String concatenation-se executa concatenarea sirurilor
7.-Translation-fiecare token(element sintactic) este analizat seman-
tic si sintactic si convertit in cod obiect
8.-Linkage-se rezolva toate solicitarile catre file externe astfel
incat sa rezulte un program executabil sau o biblioteca dinamica.
Se observa ca preprocesarea are loc inaintea analizei sintactice si a
generarii de legaturi intre elementele programului.
Daca exista erori de formulare sau incompatibilitati care blocheaza
executia comenzilor,compilatorul va genera mesaje de eroare.
-70-
Directivele de preprocesare sunt:#define,#error,#if,#elif,#else,#endif
#ifdef,#import,#include,#line,#undef si comanda nula.
#define -se utilizeaza pentru a denumi o constanta in program
In urma utilizarii,constanta respectiva(elementul din program) va fi inlo-
cuita in toate aparitiile urmatoare din program.
Sintaxa este: #define identificator(nume) token-string(element sau sir)
EXEMPLU: #define test (f1,f2)(f1*f2)
Daca numele identificatorului nu este urmat de un token,atunci identifica-
torul va fi sters din toate aparitiile sale ulterioare din program.
Exemplu: #define test va strege identificatorul "test" din program
Dostları ilə paylaş: |
|
|