Documente Academic
Documente Profesional
Documente Cultură
Suport Curs C++ II INF PDF
Suport Curs C++ II INF PDF
Dorin BoKu
2001-2002
În loc de introducere
După iniţierea în programare, cu sprijinul altor cursuri,
paralele cu programarea sau complementare în cel mai bun
înţeles al cuvântului, iată-ne bătând, cu înţelepciune
sporită, la poarta unei lumi în care vom vedea aceleaşi
concepte într-o mişcare care respectă rigorile unui joc nou,
mult mai interesant din punct de vedere al împătimiţilor în
ale programării. Avem de lămurit şi adâncit două teme de
meditaţie mari şi importante pentru formarea oricărui
informatician :
Autorul
2
I BAZELE C++. LIMBAJUL C
3
1 Privire de ansamblu asupra limbajului C
1.1 Despre originile limbajului C
Legenda spune că C a fost inventat şi implementat de Dennis Ritchie pe un
calculator DEC PDP-11, care utiliza sistemul de operare UNIX. Virtual, C este
rezultatul unui proces de dezvoltare complex, la care putem spune că a participat
întreaga comunitate preocupată de ridicarea performanţelor în munca de
programare. De fapt, strămoşi direcţi ai limbajului C pot fi considerate limbajele
BCPL (creat de Martin Richards) şi limbajul B (inventat de Ken Thompson). În
anii ’70, limbajul B a influenţat în cel mai înalt grad specificarea limbajului C. Mult
timp, standardul de facto pentru C a fost versiunea ce însoţea sistemul de
operare UNIX. Acest standard a fost descris pentru prima dată în cartea The C
Programming Language, scrisă de Brian Kernigan şi Dennis Ritchie, apărută în
1978 la editura Prentice Hall. Odată cu creşterea popularităţii calculatoarelor
personale, au fost create numeroase implementări de C, ceea ce a generat o
problemă nouă: compatibilitatea implementărilor. Pentru rezolvarea acestei
probleme, în vara anului 1983, a fost înfiinţat un comitet pentru crearea unui
standard ANSI (American National Standard Institute) care avea ca sarcină
specificarea definitivă a limbajului C. Standardul ANSI C a fost adoptat în
decembrie 1989, primele copii devenind disponibile la începutul lui 1990. La ora
actuală, toate compilatoarele C/C++ se aliniază la standardul ANSI C. Totodată
standardul ANSI C este o bază pentru propunerea de standard ANSI C++(Bazat
pe limbajul C, C++ adaugă extinderi care permit programarea obiect orientată).
Evident, vom reveni asupra topicii C++.
4
beneficiază de un cadru mult mai liberal (controalele compilatorului nu mai sunt
atât de severe ca în Pascal).
ÖSpre deosebire de un limbaj de nivel înalt, C nu face aproape nici un
control în timpul executării unui program. Responsabilitatea de a evita
producerea erorilor şi de a le dibui şi corecta, dacă le-a ocazionat, este a
programatorului.
ÖC permite lucrul direct cu biţi, octeţi cuvinte şi pointeri ceea ce îl face
potrivit pentru programare la nivel de sistem.
5
algoritmi cu limpezime, eleganţă şi eficienţă. În C un bloc de cod poate fi creat
incluzând între acolade un grup de instrucţiuni.
6
Auto double Int struct
Break else Long switch
Case enum Register typedef
Char extern Return union
Const float Short unsigned
Continue for Signed void
Default goto Sizeof volatile
Do if Static while
Tabelul 1 Cele 32 cuvinte-cheie definite de standardul ANSI C
Toate programele C constau din una sau mai multe funcţii a căror
sintaxă şi semantică o vom prezenta în secţiunile următoare.
7
să rezolve anumite probleme. Această nouă abordare o vom dezbate treptat în
secţiunile care vor urma.
8
2 Expresii în C
În această secţiune vom trece în revistă propunerile limbajului C în ceea ce
proveşte posibilitatea de a utiliza expresii. Toate limbajele de programare
acordă o atenţie specială noţiunii de expresie deoarece, în ultimă analiză, orice
transformare a datelor de intrare ale unui program este realizată prin intermediul
uneia sau a mai multor expresii. Aşa cum vom descoperi pe parcurs, C este un
limbaj mult mai flexibil decât alte limbaje în materie de definire şi utilizare a
expresiilor. Ceea ce este foarte limpede în acest moment este faptul că
expresiile sunt realizate cu ajutorul elementelor atomice numite date şi
operatori. Datele pot fi reprezentate cu ajutorul constantelor sau prin intermediul
variabilelor. Ajungem, evident, la problema tipurilor de date suportate de C.
9
Tip de dată Dimensiune reprezenatre Domeniul valoric minimal
în biţi
char 8 -127..127
unsigned char 8 0..255
signed char 8 -127..127
int 16 -32767..32767
unsigned int 16 0..65535
signed int 16 Similar cu int
short int 16 Similar int
unsigned short int 16 0..65535
signed short int 16 Similar cu short int
long int 32 -2.147.483.647..2.147.483.647
signed long int 32 Similar cu long int
unsigned long int 32 0..4.294.967.295
float 32 6 zecimale exacte
double 64 10 zecimale exacte
long double 80 10 zecimale exacte
10
literă sau o liniuţă de subliniere; următoarele pot fi litere, cifre sau liniuţa de
subliniere.
Exemple de identificatori:
Corecţi Incorecţi
număr_de pagini 1_valoare_returnată
_limita1 ok!
ok_ switch..conversie
2.4 Variabile C
Aşa cum s-a aflat şi în alte împrejurări, o variabilă este numele unei locaţii de
memorie utilizată pentru a păstra o valoare care poate fi modificată de
program.
Înainte de a fi utilizate în program, variabilele trebuie declarate. Declararea unei
variabile în C are forma:
<Tip> <Listă_de_variabile>;
<Tip> trebuie să fie un tip de dată valid (predefinit sau definit de utilizator)
precedat, eventual, de un specificator de conversie.
<Listă_de_variabile> poate consta dintr-un nume de identificator sau mai multe
nume de identificatori separate prin virgulă.
Exemple de declaraţii de variabile:
int nr_pag;
char opt,ch;
unsigned i, j;
11
2.5 Variabile locale în C
Variabilele declarate în interiorul unei funcţii sunt numite variabile locale. O
parte din literatura C/C++ numeşte aceste variabile automatice. Ţinând cont de
asemănarea evidentă cu variabilele locale din Pascal, de exemplu, vom folosi
termenul de variabilă locală.
Afirmaţia cea mai importantă referitor la variabilele locale este următoarea:
“Variabilele locale există cât timp se execută blocul de cod în care sunt
declarate”.
Altfel spus, o variabilă locală este creată la începerea execuţiei blocului său şi
este distrusă la încheierea execuţiei acestui bloc.
Blocul de cod cel mai uzual în care se declară variabile este funcţia.
Deoarece noţiunea de funcţie este centrală pentru fizionomia unui program C,
prezentăm, în continuare, o viziune asupra unui program C care include şi
noţiunea de funcţie.
Directive preprocesor
Declaraţii globale
Declaraţie funcţie_1
:
Declaraţie funcţie_n
int main()
{
Declaraţii de date ale programului principal
Instrucţiuni program principal
Orice program C conţine obligatoriu o funcţie al cărei nume este main
}
Implementare funcţie_1
:
Implementare funcţie_n
12
care nu mai insistăm deoarece standardul ANSI C ca şi standardul ANSI C++
descurajează, respectiv interzice utilizarea căii clasice.
În C++ declararea unei funcţii se face apelând la prototipuri.
În sfârşit, implementarea unei funcţii înseamnă o construcţie sintactică de tipul:
<Antet funcţie>
{
<Declaratii de date>
<Instructiuni executabile>
}
void f1(void)
{
int x;
x=12;
}
void f2(void)
{
int x;
x=-128;
}
Variabila x este declarată atât în f1 cât şi în f2. Variabila x din f1 nu are nici o
influenţă asupra variabilei x din f2 deoarece fiecare variabilă este cunoscută
doar în blocul de cod gazdă. Din obişnuinţă şi din respect faţă de tradiţie,
majoritatea programatorilor declară variabilele folosite de o funcţie imediat după
acolada deschisă a funcţiei şi înainte de orice instrucţiune executabilă. De fapt,
se pot declara variabile în orice bloc de cod, obţinându-se chiar efecte benefice
pentru calitatea codului rezultat. Astfel putem avea:
void fexemplu(void)
{
int i;
printf(”Introduceti un numar:”);
13
scanf(“%d”,&i);
if (i>0)
{
char nume [40];
printf(”Numele Dvs.:”);
gets(nume);
}
}
Din exemplul de mai sus reţinem doar faptul că, în situaţia în care numărul
preluat de la tastatură (cu ajutorul rutinei de uz general scanf ) este mai mare
decât zero, atunci devine activ blocul de cod în care se declară variabila nume.
Această variabilă este distrusă la ieşirea din acest bloc de cod.
Prin urmare, un avantaj potenţial: variabilei nume i se alocă memorie numai
dacă se activează blocul de cod, ceea ce înseamnă economie de memorie.
Este absolut evident faptul că se declară într-un bloc de cod una sau mai multe
variabile strict necesare în acest bloc de cod; în acest mod prevenim, într-o
oarecare măsură, apariţia unor efecte secundare. Mai semnalăm o diferenţă
importantă între modul de declarare a variabilelor locale în C faţă de C++.
În C trebuie declarate toate variabilele locale la începutul blocului în care
intenţionăm să le definim, înainte de orice instrucţiune executabilă. În C++ nu
mai există această restricţie. Astfel că secvenţa de cod C:
void f(void)
{
int i;
i=10;
int j;
j=20;
}
14
2.7 Variabile globale
Variabilele globale, spre deosebire de cele locale, sunt cunoscute în tot
programul şi pot fi utilizate de către orice zonă a codului. Totodată, ele îşi
păstrază valoarea tot timpul execuţiei programului. Variabilele globale se
crează prin declarare în afara oricărei funcţii. Orice expresie are acces la ele,
indiferent de tipul blocului de cod în care se află expresia. De semnalat faptul că
o variabilă globală este vizibilă în orice bloc de cod al programului cât timp în
respectivul bloc de cod nu a fost declarată o variabilă cu acelaşi nume, de
acelaşi tip. În caz afirmativ, în blocul de cod este prioritară referirea la variabila
locală.
Stocarea variabilelor globale este făcută de compilator într-o zonă de memorie,
special alocată. Variabilele globale sunt utile atunci când mai multe funcţii ale
aceluiaşi program folosesc aceleaşi date. Utilizarea excesivă a variabilelor
globale ar trebui evitată deoarece:
ÖOcupă memoria pe tot timpul execuţiei programului, nu doar când sunt
necesare;
ÖDacă o funcţie utilizează o variabilă globală în locul uneia locale, atunci
funcţia îşi pierde din generalitate, bazându-se pe ceva exterior logicii ei;
ÖVariabile globale multe înseamnă efecte secundare necunoscute şi
nedorite.
15
fi modificată de condiţii externe, fiind protejată de modificări accidentale în
programul în care este declarată”.
extern
Deoarece C/C++ permit secţiunilor separate ale unui program să fie compilate
independent şi să li se editeze legăturile împreună, trebuie să existe o modalitate
de a comunica tuturor fişierelor variabilele globale necesare programului.
Scenariul căruia trebuie să îi facem faţă este următorul: “Cum putem informa
toate fişierele care compun un program C despre variabilele globale utilizate?”.
Soluţia este următoarea : Se declară variabilele globale într-un fişier, care este
de preferat să conţină şi funcţia principală a programului, aceleaşi variabile
declarându-se şi în toate fişierele care folosesc variabilele globale, însoţite de
specificatorul extern.
static
Variabilele de tip static sunt variabile permanente în interiorul funcţiei sau
fişierului în care se găsesc. Spre deosebire de variabilele globale, ele nu sunt
cunoscute în afara funcţiei sau fişierului, dar îşi păstrează valoarea între două
apelări. Această caracteristică este folositoare la scrierea de funcţii generice şi
de bibliotecă , utilizabile de alte programe. Specificatorul static are efecte diferite
asupra variabilelor locale şi globale.
16
Aplicând specificatorul static unei variabile globale, cerem compilatorului să
creeze o variabilă globală care este cunoscută doar în fişierul în care a fost
declarată. Aceasta înseamnă că, deşi variabila este globală, rutine din alte
fişiere nu au acces la ea şi nu îi pot modifica conţinutul.
register
Specificatorul de stocare register se aplică, prin tradiţie, doar variabilelor de tip
int şi char. Totuşi standardul ANSI C îi dă definiţia astfel încât poate fi folosit
pentru orice tip de variabilă. Iniţial, register cerea compilatorului să păstreze
valoarea unei variabile într-un registru din CPU, nu în memoria RAM, unde
variabilele sunt stocate, de regulă. Scopul unei astfel de cereri: spor de viteză în
timpul operaţiilor asupra variabilei. Actualmente, definiţia specificatorului register
a fost mult extinsă, acesta putând fi aplicat oricărui tip de variabilă. Standardul
ANSI C stipulează, simplu, că register este o indicaţie dată compilatorului că
obiectul vizat va fi utilizat preferenţial din punct de vedere al vitezei.
Specificatorul se poate aplica doar variabilelor locale şi parametrilor formali. Prin
urmare, nu sunt permise variabile globale de tip register.
auto
Specificatorul auto este, practic, nefolosit, fiind reclamat doar de motive de
compatibilitate între codul C şi codul C++.
Exemple:
char ch=’A’;
int media=0;
float bilant=0;
Constante C
17
Constantele se referă la valori fixe pe care programul nu poate să le modifice.
Constantele pot fi de oricare din tipurile fundamentale de date. Modul în care se
reprezintă în program fiecare constantă depinde de tipul său.
Constantele de tip caracter sunt incluse între ghilimele simple. ‘A’ şi ‘%’ sunt
exemple de constante de tip caracter. C defineşte şi caractere multioctet, în
mediile care nu utilizează limba engleză. O situaţie asemănătoare se întâlneşte
şi în Delphi.
Constantele de tip întreg sunt specificate ca numere fără parte fracţionară. De
exemplu, 3 şi -15 sunt exemple de constante întregi.
Constantele în virgulă mobilă cer punctul zecimal urmat de partea zecimală a
numărului. 0.75 este un exemplu de constantă care apelează la virgula mobilă.
Limbajul C permite şi notaţia ştiinţifică, cunoscută şi în alte limbaje.
De asemenea, C stabileşte pentru o constantă numerică cel mai scurt tip de date
compatibil care o poate păstra. Astfel că, -10 este implicit un int, 60.000 este
unsigned int iar 120.000 este un long int. Tipul constantei numerice poate fi
specificat şi explicit ca în exemplele din Tabelul 3.
După cum se vede, constantele în virgulă mobilă sunt asimilate implicit tipului
double.
18
Constante de tip şir
C admite, cum era şi firesc şi constanta de tip şir de caractere. O constantă şir
este o succesiune de caractere delimitată de ghilimele. A nu se confunda şirurile
de caractere cu caracterele. ‘a’ şi “a” sunt constante de tipuri diferite( disticţie
care, în Pascal, nu este operată sintactic, vorbind.
Codul Semnificaţia
\b Backspace
\f form feed (=Salt la pagină nouă)
\n CR+LF
\r CR
\t tab orizontal
\’’ Ghilimele
\’ Apostrof
\0 Null
\\ Backslash
\v tabulare verticală
\a Alertă
\N constantă în octal; N este constanta
\xN constantă în hexazecimal; N este constanta
Tabelul 4 Coduri backslash în C
2.11 Operatori în C
C dispune de foarte mulţi operatori. De fapt, C acordă acestora o importanţă mult
mai mare în comparaţie cu alte limbaje. C defineşte, în esenţă, patru clase de
operatori: aritmetici, relaţionali, logici şi de acţiune la nivel de bit. Pentru
anumite sarcini, C are operatori suplimentari.
Operatorul de atribuire
Operatorul de atribuire poate fi folosit, în C, în cadrul oricărei expresii valide,
lucru care nu este permis în majoritatea limbajelor de programare (inclusiv
Pascal), care tratează operatorul de atribuire ca pe un caz de instrucţiune
specială. Sintaxa de aplicare a operatorului de atribuire în C este:
<Nume_variabilă> = <Expresie>;
19
Membrul stâng al atribuirii trebuie să fie o variabilă sau un pointer, nu o funcţie
sau o constantă.
int nri;
char car;
float nrr;
void functie(void)
{
car=nri; /* Linia 1 */
nri=nrr; /* Linia 2 */
nrr=car; /* Linia 3 */
nrr=nri; /* Linia 4 */
}
În Linia 1, în variabila car ajung primii 8 biţi cei mai puţin semnificativi ai
variabilei nri. Dacă nri este cuprins între 0 şi 255, car şi nri vor avea valori
identice ş.a.m.d.. Presupunând că sistemul de calcul are cuvântul de 16 biţi,
Tabelul 5 prezintă posibilele pierderi de informaţie care pot să apară la
conversia de tip în atribuiri.
Atribuiri multiple
C permite atribuirea aceleeaşi valori mai multor variabile prin utilizarea atribuirii
multiple într-o singură instrucţiune de atribuire. De exemplu instrucţiunea:
x = y = z = 0;
20
permite setarea la 0 a variabilelor x,y,z. Programatorii profesionişti folosesc
masiv atribuirea multiplă pentru a obţine cod performant.
Să mai adăugăm o observaţie interesantă pentru un programator profesionist. O
atribuire de tipul:
<Variabilă>=<Variabilă>+<Expresie> (1)
este echivalentă semantic cu sintaxa:
<Variabilă>+=<Expresie> (2)
Deşi s-ar putea să vină “peste mână” unor programatori, aceştia trebuie să ştie
că pentru compilator sintaxa (2) este de preferat sintaxei (1) din punct de vedere
al calităţii codului generat.
Aşadar,
x=x+1
este totuna cu:
x+=1.
Operatori aritmetici
Tabelul 6 prezintă operatorii aritmetici din C.
Operator Acţiune
- Scădere, de asemenea şi minus unar
+ Adunare
* Înmulţire
/ Împărţire
% Modul
-- Decrementare
++ Incrementare
Tabelul 6 Operatori aritmetici în C
21
Există, totuşi, o diferenţă între forma cu prefix şi forma cu sufix, dacă operatorii
sunt utilizaţi într-o expresie. Atunci când operatorii preced variabila, C
efectuează operaţiile corespunzătoare lor, înainte de a evalua expresia.
Atunci când operatorii succed variabila, C efectuiază operaţiile corespunzătoare
lor, după evaluarea expresiei. Majoritatea compilatoarelor C/C++ produc rapid
un cod obiect eficient pentru operaţiile de incrementare şi decrementare (cod
mai bun decât cel obţinut prin utilizarea atribuirii clasice echivalente). Pe acest
considerent, recomandarea de a utiliza aceşti operatori de câte ori este posibil
este firească. În sfârşit, ordinea de precedenţă a operatorilor aritmetici este:
Operatori relaţionali
Operator Acţiune
> Mai mare decât
>= Mai mare sau egal
< Mai mic decât
<= Mai mic sau egal
== Egal
!= Diferit
Operatori logici
Operator Acţiune
&& AND (ŞI)
|| OR (SAU)
! NOT (NEGAT)
Tabelul 7 Operatorii relaţionali şi logici în C.
22
De ordinul cel mai înalt !
>, >=, <, <=
==, !=
&&
De ordinul cel mai coborât ||
Tabelul 8 Relaţia de precedenţă între operatorii relaţionali şi logici
Evident, parantezele pot fi utilizate pentru a modifica ordinea firească de
evaluare a unei expresii relaţionale şi/sau logice.
Operator Acţiune
& AND
| OR
^ OR exclusiv (XOR)
~ Complement faţă de 1 (NOT)
>> Deplasare la dreapta
<< Deplasare la stânga
Tabelul 9 Operatorii de acţiune pentru biţi
<Operand_1> <Operator><Operand_2>
Operatorii de deplasare a biţilor >> şi << deplasează toţi biţii dintr-o variabilă la
dreapta sau la stânga. Sintaxa asociată este:
23
Sensul operaţiei de shiftare este cel precizat la un curs de Bazele logice ale
sistemelor de calcul.
♦
Precizări referitoare la operatorii suplimentari pot fi găsite în [1].
Operatorul ?
C propune un operator unar foarte puternic şi util care poate înlocui anumite
instrucţiuni de forma “dacă – atunci – altfel”.
z Se evaluiază <Expresie_1>;
z Dacă <Expresie_1> este adevărată se evaluiază <Expresie_2> şi
rezultatul evaluării ei i se atribuie expresiei globale;
z Dacă <Expresie_1> este falsă. atunci se evaluiază <Expresie_3> şi
rezultatul evaluiării acesteia este atribuit expresiei globale.
Această semantică este evidenţiată şi de exemplul de mai jos de utilizare
a operatorului ?.
X=24;
X=X<24 ? 1:X+1;
X=24;
if (X<24) X=1
else X=X+1;
Operatorii & şi *
Un pointer este adresa din memorie a unei variabile. O variabilă de tip pointer
este declarată explicit pentru a reţine un pointer către un obiect de un tip
specificat. Cunoaşterea adresei unei variabile poate fi de mare utilitate în
anumite situaţii. În C pointerii au trei funcţii principale.
24
O atribuire de tipul :
adr=&Numar_Elemente;
este utilă pentru a obţine în variabila adr adresa din memorie a variabilei
Numar_Elemente. Această adresă este adresa locaţiei din memorie începând de
la care se păstrează conţinutul variabilei Numar_Elemente.
Al doilea operator pentru pointeri este * , oarecum complementar operatorului
&. Şi operatorul * este unar, returnând valoarea din variabila localizată la
adresa specificată. Astfel , după execuţia secvenţei:
adr=&Numar_Elemente;
nr=*adr ;
Exemplu
#include <stdio.h>
void main(void)
{
int destinatar,sursa;
int *m;
sursa=10;
m=&sursa;
destinatar=*m;
printf (“%d”, destinatar);
}
25
sizeof este un operator unar utilizat în timpul compilării care returnează lungimea
în octeţi a variabilei sau a specificatorului de tip dintre parantezele asociate de
sintaxă operatorului. Sintaxa de utilizare a operatorului:
sizeof ( <Nume_Variabila>)
sau
sizeof <Nume_Variabila>
struct tangajat
{
char nume[80];
int varsta;
float salariu;
} angajat;
struct tangajat *pangajat=&angajat;
angajat . salariu:=1100000;
pangajat->salariu=1100000;
26
Un comentariu este o notă explicativă pentru programatori, ignorată complet de
compilator În C comentariile sunt delimitate de perechile de caractere /* …*/.
Compilatorul C ignoră orice text aflat între aceşti delimitatori.
Exemple de comentarii:
sau
Compilatoarele C++ acceptă şi comentarii care încep cu secvenţa //, dar se pot
întinde pe o singură linie, astfel:
27
3 Reprezentarea structurilor de prelucrare în C/C++
Limbajul C nu propune o revoluţie de fond în ceea ce priveşte problema
reprezentării structurilor de prelucrare. Vine, însă, cu o serie de inovaţii
interesante, în ceea ce priveşte flexibilitatea şi varietatea propunerilor de
reprezentare. Nu spunem nici o noutate amintind că reprezentarea structurilor
de prelucrare se bazează pe combinarea, dacă se poate, profesională, a
instrucţiunilor executabile ale limbajului. Potrivit standardelor ANSI C şi ANSI
C++ instrucţiunile sunt împărţite în următoarele grupe:
ÖInstrucţiuni de selecţie
ÖInstrucţiuni iterative
ÖInstrucţiuni de salt
ÖInstrucţiuni de etichetare
ÖInstrucţiuni bloc
Instrucţiunile de selecţie cuprind enunţurile if şi switch.
În categoria instrucţiunilor iterative intră enunţurile while, for şi do-while.
Instrucţiunile de salt desemnează în C instrucţiunile break, continue, goto şi
return.
Instrucţiunile de etichetare includ enunţurile case şi default (implicate, esenţial,
în sintaxa şi semantica instrucţiunii switch şi etichetele (discutate relativ la
instrucţiunea goto. Problematica instrucţiunilor expresie am discutat-o, pe larg,
în secţiunea precedentă. De asemenea, blocul de cod este, deja, o noţiune cu
care ne-am familiarizat. De altfel, aşa cum în Pascal, secvenţa de cod cuprinsă
între begin şi end era numită şi instrucţiune compusă, standardul ANSI C++
propune, aceeaşi denumire, ca denumire alternativă pentru blocul de cod.
28
3.2 Instrucţiuni de selecţie în C
C admite două tipuri de instrucţiuni de selecţie: if şi switch.
if
Sintaxa generală a instrucţiunii if este următoarea:
if (<Expresie>) {…}
else {…};
29
Instrucţiunea if poate fi imbricată. În caz de imbricare, clauza else se referă
întotdeauna la cea mai apropiată instrucţiune if situată în amonte, în acelaşi bloc
cu else şi neasociat încă cu un else, ca în exemplul:
#include <stdio.h>
#include <conio.h>
#include <string.h>;
void main()
{
float A,B,X;
clrscr();
gotoxy(20,12);
printf("____________________________________");
gotoxy(20,13);
printf(" Rezolvarea unei ecuatii de gradul I");
gotoxy(20,14);
printf(" A=");
gotoxy(20,15);
printf(" B=");
gotoxy(20,17);
printf("____________________________________");
gotoxy(20+strlen(" A="),14);
scanf("%f",&A);
gotoxy(20+strlen(" B="),15);
scanf("%f",&B);
gotoxy(20,17);
printf("_____________________________________");
if (A==0)
if (B==0)
{
gotoxy(20,16);
printf("Ecuatie nedetrminata!!!");
getch();
}
else
{
gotoxy(20,16);
printf("Ecuatie imposibila!!!"); getch();
}
else
{
X=B/A;
gotoxy(20,16);
printf("Solutia ecuatiei este:%f",X); getch();
}
}
30
Destul de frecvent instrucţiunea if este utilizată şi în construcţia numită
“scara if-else-if” având sintaxa generală:
if (<Expresie_1>)
<Instrucţiune_1>;
else if (<Expresie_2>)
<Instrucţiune_2>;
else if (<Expresie_3>)
<Instrucţiune_3>;
:
:
else if (<Expresie_k>)
<Instrucţiune_k>;
:
Reamintim că, în anumite situaţii, putem utiliza operatorul ? pentru a înlocui
instrucţiunea if-else de forma:
if (<Expresie>)
<Expresie_1>;
else
<Expresie_2>;
cu o construcţie de forma:
<Expresie>?<Expresi_1>:<Expresie_2>;
switch
C permite reprezentarea structurilor alternative cu mai multe ramuri utilizând în
acest scop instrucţiune switch având sintaxa:
switch (<Expresie>) {
case <Constanta_1>:
<Secvenţa_de_instrucţiuni_1>
break;
case <Constanta_2>:
<Secvenţa_de_instrucţiuni_2>
break;
:
case <Constanta_k>:
<Secvenţa_de_instrucţiuni_k>
break;
default
<secvenţa_de_instrucţiuni>
}
31
Semantica instrucţiunii este următoarea: se compară valoarea expresiei
<Expresie> cu valorile constantelor specificate în instrucţiunile case. Când se
întâlneşte o coincidenţă, se execută secvenţa de instrucţiuni asociată acelui
case până la instrucţiunea break sau până când se ajunge la finalul instrucţiunii
switch. Instrucţiunea default se execută dacă nu este întâlnită nici o
coincidenţă. Clauza default este opţională şi dacă nu este prezentă, atunci când
nu avem nici o coincidenţă nu se execută nici o acţiune.
Standardul ANSI C stipulează că switch poate să aibă cel mult 257 de clauze
case. Standardul propus de ANSI C++ recomandă să se poată introduce cel mult
16.384 clauze case. În practică, din motive de eficienţă, se urmăreşte limitarea
numărului de clauze case într-o instrucţiune switch.
Instrucţiunea case nu poate fi utilizată decât în contextul instrucţiunii switch.
Instrucţiunea break este o instrucţiune de salt în C, fiind utilizată pentru a
determina ieşirea forţată din anumite tipuri de structuri (switch, for, do-while).
Ilustrăm modul de utilizare a instrucţiunii switch prin exemplul de mai jos.
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
void main()
{
int x;
char Ras;
clrscr();
x=random(2);
switch(x+2){
case 3:
{
gotoxy(20,10);
printf("Switch atins...");
break;
}
default:
{
gotoxy(20,10);
printf("Switch ocolit...");
}
}
Ras=getch();
}
32
-Dacă în instrucţiunea switch sunt utilizate constante de tip caracter, ele
sunt automat convertite în întregi.
O astfel de sintaxă permite multe variante constructive ale buclei for. În general
vorbind, însă:
-<Iniţializare> este o instrucţiune de atribuire utilizată pentru a iniţializa
variabila de control a buclei;
-<Condiţie> este o expresie relaţională care determină condiţia de ieşire
din buclă;
-<Increment> defineşte modul în care se modifică variabila de control a
buclei de fiecare dată când aceasta se repetă.
De remarcat faptul că cele trei secţiuni trebuie separate prin punct şi virgulă.
Aşadar, bucla for se execută cât timp <Condiţie> este adevărată. Dacă
<Condiţie> devine falsă, atunci execuţia programului continuă cu instrucţiunea
care urmează construcţiei for, dacă aceasta există.
Maniera clasică de utilizare a buclei for în C, o prezentăm în exemplul de mai
jos.
#include <stdio.h>
#include <iostream.h>
#include <conio.h>
long int fact( int n);
void main()
{
clrscr();
gotoxy(20,12);
int nr;
cout<<"Introduceti numarul:";
cin>>nr;
33
gotoxy(20,14);
cout<<"Factorial("<<nr<<")="<<fact(nr);
getch();
}
long int fact(int n)
{
long int f;
int i;
f=1;
/*-------------------------------------------------------------------
/* Bucla for clasică cu o singură variabilă de control
/*-------------------------------------------------------------------
for (i=2;i<=n;i++)
f=f*i;
return f;
}
Exemplul de cod C prezentat mai sus permite calculul factorialului pentru un
număr natural dat. Exemplul arată, anticipând, modul de utilizare a conceptului
de funcţie în C++.
#include <conio.h>
#include <stdio.h>
#include <string.h>
#include <dos.h>;
34
void converg(int linie, char *mesaj);
//Functia principala
void main()
{
clrscr();
gotoxy(20,12);
converg(12,"Acesta este un test pentru functia converg().");
}
for (i=ls,j=cs;i<=j;i++,j--)
{
gotoxy(i,linie);printf("%c",mesaj[i-ls]);
delay(50); //prototipul in fisierul antet <dos.h>
gotoxy(j,linie);printf("%c",mesaj[strlen(mesaj)-1-cs+j]);
}
getch();
}
for ( ; ; )
35
#include <stdio.h>
#include <conio.h>
#include <ctype.h> //Contine prototipul functiei toupper
void main()
{
int sw;
char ras;
sw=1;
/* Bucla while în acţiune…
while(sw)
{
gotoxy(20,12);
printf("Continuam(D,N):");
ras=getch();
if (toupper(ras)=='N')
sw=0;
}
}
do {
<Instrucţiune>;
} while <Condiţie>;
Bucla do-while se repetă până când <Condiţie> devine falsă. Dăm, mai jos, un
exemplu practic de utilizare a buclei do-while (afişarea/ selectarea opţiunilor unui
program C).
:
char prelopt()
{
int c;
clrscr();
gotoxy(20,9);
printf( "Optiunile programului...");
gotoxy(20,11);
printf("1-Preluare elemente vector");
gotoxy(20,12);
printf("2-Determinare suma");
gotoxy(20,13);
printf("3-Terminare program");
36
gotoxy(20,15);
printf("Optiunea Dvs.:");
/* Utilizare buclă do-while…
do
c=getch();
while ((c!='1')&&(c!='2')&&(c!='3'));
return c;
};
:
Alte instrucţiuni C…
Aşa cum rezultă şi din exemplele prezentate, în C mai sunt intens folosite
următoarele instrucţiuni: return, exit, break , continue şi goto.
return [<Expresie>]
Funcţia exit()
exit (<Cod_de_retur>);
Instrucţiunea break
Are două utilizări. Poate fi folosită, după cum am văzut deja pentru a încheia un
case dintr-o instrucţiune switch, sau pentru a determina încheierea imediată a
unei bucle.
Instrucţiunea continue
Forţează trecerea la următoarea iteraţie a unei bucle, determinând ignorarea
restului codului iteraţiei în care se află.
Nu facem menţiuni speciale referitor la instrucţiunea goto.
37
38
4 Operaţii I/O relativ la perifericele standard
Ne propunem în acest modul să prezentăm funcţiile C care fac parte din
sistemul I/O referitor la efectuarea operaţiilor de introducere a datelor de la
tastatură şi afişare a acestora pe ecranul monitorului.
Funcţia getch() este utilizată pentru a citi un caracter de la tastatură fără ecou pe
ecranul monitorului , analog mecanismului readkey din Pascal.
#include <stdio.h>
#include <conio.h>
#include <ctype.h>
void main()
{
char car;
do
{
car=getche();
car=toupper(car);
putchar(car);
39
}
while (car!='\033');
}
Prototipul funcţiei toupper() se află (aşa cum am mai spus-o) în fişierul antet
ctype.h. Se observă că citirea se face cu ecou pe ecranul monitorului. Este cazul
să spunem că programatorul în C/C++ are de făcut o serie de descoperiri
folositoare în ceea ce priveşte oferta numeroaselor fişiere antet livrate odată cu
compilatorul. Prezentarea tuturor acestor funcţii, fie şi numai prina antet, nu este
de loc o treabă uşoară, din punct de vedere al volumului. De aceea. în acest
suport de curs adresăm cititorului invitaţia de a descoperi, cu ajutorul help-ului on
line şi a unor cărţi care nu fac economie de spaţiu, potenţialul structurat în
fişierele antet.
#include <stdio.h>
#include <conio.h>
void main()
{
char s[30];
clrscr();
gotoxy(20,10);
printf("Introduceti un sir de caractere:");
gets(s);
gotoxy(20,11);
printf("Sirul introdus :");
puts(s);
getch();
clrscr();
}
40
#include <stdio.h>
#include <conio.h>
void main()
{
char s;
char *p;
clrscr();
gotoxy(20,10);
p=&s
printf("Introduceti un sir de caractere:");
gets(p);
gotoxy(20,11);
printf("Sirul introdus :");
puts(p);
getch();
clrscr();
}
int scanf( const char *format [, address, ...]); {Fişierul antet gazdă :stdio.h}
int printf(const char *format [, argument, ..]); {Fişierul antet gazdă :stdio.h}
printf()
Returnează, cu destinaţia ecran, numărul de caractere scrise sau, dacă apare o
eroare, o valoare negativă. Parametrul format se compune din două tipuri de
simboluri. Primul tip îl formează caracterele care vor fi afişate pe ecran. Al doilea
tip se referă la specificatorii de format cu ajutorul cărora se stabileşte modul în
care sunt afişate argumentele care urmează. Un specificator de format începe cu
un semn % şi este urmat de un cod de format. Trebuie să existe acelaşi număr
de argumente ca şi acela al specificatorilor de format şi, totodată, specificatorii
de format şi argumentele se asociază în ordinea de la stânga la dreapta.
41
Codurile de format disponibile pentru printf() sunt:
Cod Format
%c Caracter
%d Numere întregi în baza 10, cu semn
%I Numere întregi în baza 10, cu semn
%e Notaţie ştiinţifică (cu litera e)
%E Notaţie ştiinţifică (cu litera E)
%f Număr zecimal în virgulă mobilă
%g Foloseşte %e sau %f , anume, care din ele este mai mic
%G Foloseşte %E sau %f , anume, care din ele este mai mic
%o Număr în octal, fără semn
%s Şir de caractere
%u Numere întregi zecimale fără semn
%x Numere hexazecimale fără semn (cu litere mici)
%X Numere hexazecimale, fără semn, cu litere mari
%p Afişează un pointer
%n Argumentul asociat este un pointer de tip întreg în care a fost
plasat numărul de caractere scrise până atunci.
%% Afişează un semn %
#include <stdio.h>
#include <conio.h>
void main()
{
int numara;
printf("Acesta%n este un test...\n",&numara);
printf("%d",numara);
getch();
}
#include <stdio.h>
#include <conio.h>
void main()
{
int numara;
42
clrscr();
printf("Acesta%n este un test...\n",&numara);
printf("Aliniere la dreapta.......\n");
printf("%10d\n",numara);
printf("Aliniere la stanga .......\n");
printf("%-d\n",numara);
printf("Completare cu zerouri.....\n");
printf("%010d\n",numara);
getch();
}
scanf()
Este o rutină de uz general pentru intrări de la consolă. Ea poate să citească
toate tipurile de date încorporate şi să facă automat conversia numerelor în
format intern corect. Se aseamănă mult cu complementara ei printf(). Ca funcţie,
scanf() returnează numărul de elemente cărora li s-a atribuit cu succes o
valoare. Dacă apare o eroare scanf() returnează EOF. Argumentul format
determină modul în care vor fi citite valorile în variabilele din lista de argumente.
Codurile de format disponibile pentru scanf() sunt prezentate în tabelul de mai
jos.
Cod Format
%c Citeşte un singur caracter
%d Citeşte un număr întreg în baza 10
%I Citeşte un număr întreg în baza 10
%e Citeşte un număr în virgulă mobilă
%E Citeşte un număr în virgulă mobilă
%f Citeşte un număr în virgulă mobilă
%g Citeşte un număr în virgulă mobilă
%o Citeşte un număr în octal
%s Citeşte un şir de caractere
%u Numere întregi zecimale fără semn
%x Citeşte un număr în hexazecimal
%p Citeşte un pointer
%n Argumentul asociat este un pointer de tip întreg în care a fost
plasat numărul de caractere citite până atunci.
%u Citeşte un întreg fără semn
%[] Caută un set de caractere
Tabelul 10. O parte din codurile pentru formatarea operaţiilor I/I relativ la
periferice standard
Primul exemplu de cod prezentat mai jos ilustrează ideea de scanset posibilă la
utilizarea funcţiei scanf(). Definirea unui scanset înseamnă, de fapt că citirea
corespunzătoare unei variabile este validată cât timp caracterele citite se
potrivesc celor definite în scanset.
43
Al doilea exemplu este conceput în ideea că valoarea variabilei n2 va fi citită
numai după introducerea de la tastatură a două caractere virgulă. Acest
mecanism permite, printre altele, definirea ca separator la introducerea datelor a
altui caracter decât <CR>.
#include <stdio.h>
#include <conio.h>
void main()
{
char sir[3];
clrscr();
#include <stdio.h>
#include <conio.h>
void main()
{
int n1,n2;
clrscr();
scanf("%d,,%d",&n1,&n2);
printf("%d\n",n1);
printf("%d\n",n2);
getch();
}
44
Constanta Valoare Mod Text Caracterisrici
LASTMODE -1 Precedentul mod text
BW40 0 Black and white 40 coloane
C40 1 Color 40 coloane
BW80 2 Black and white 80 coloane
C80 3 Color 80 coloane
MONO 7 Monochrome 80 coloane
C4350 64 EGA and VGA 50 linii
Tabelul 11. Moduri video text standard
Aceste moduri text pot fi selectate cu ajutorul funcţiei textmode(), al cărei antet
se află tot în conio.h şi face parte tot din arsenalul C de lucru cu ecranul în mod
text. Tot în mod text, putem controla culorile textului şi ale fondului (background-
ul). Sintaxa funcţiilor cu care realizăm controlul culorilor este:
textcolor(<Culoare>);
textbackground(<Culoare>);
Parametrul <Culoare> poate lua una din valorile prezentate în tabelul de mai jos:
45
clreol() – se utilizează pentru ştergerea liniei curente începând cu coloana
pe care se află cursorul şi până la sfârşitul liniei.
gotoxy(col,lin) – permite poziţionarea cursorului pe linia şi coloana
specificate.
De asemenea, pot fi de interes, în anumite situaţii, următoarele funcţii:
Öwindow(css,lss,cdj,ldj);
Defineşte coordonatele ferestrei text active.
Öwherex();
Returnează coloana curentă a cursorului.
Öwherey();
Returnează linia curentă a cursorului.
Ögettext() şi puttext()
Permit citirea/scrierea memoriei video în mod text, ca în exemplul de mai
jos.
#include <conio.h>
//Ştergere ecran
clrscr();
gotoxy(1, 25);
cprintf(" Apasati o tasta pentru a restaura ecranul...");
getch();
46
//Refacere conţinut memorie video, utilizând datele salvate în variabila buffer
puttext(1, 1, 80, 24, buffer);
gotoxy(1, 25);
cprintf("Apasati o tasta pentru a termina...");
getch();
return 0;
}
#Include “facilcrt.h”
#include <conio.h>
#include <stdio.h>
#include <string.h>
//Activare video-invers
void avideo()
{
textcolor(BLACK);
textbackground(WHITE);
}
//dezactivare video-invers
void dvideo()
{
textcolor(WHITE);
textbackground(BLACK);
}
47
gotoxy(col,linia);
cprintf(sir);
}
}
48
5 Matrice şi şiruri
O matrice este o colecţie de variabile de acelaşi tip, apelate cu acelaşi nume.
Accesul la un anumit element al matricei se face cu ajutorul unui indice. În C
toate matricile constau în locaţii de memorie contigue. Cel mai mic indice
corespunde primului element iar cel mai mare ultimului element. Matricele pot
avea una sau mai multe dimensiuni. Ca şi în alte limbaje, cea mai simplă matrice
este şirul. În C şirul este o matrice de caractere terminate cu un caracter NULL.
Această caracteristică oferă limbajului C mai multă putere şi eficienţă decât
posedă alte limbaje.
Semnalăm, de asemenea, faptul că în C, există o strânsă legătură între matrice
şi pointeri.
<Tip> <Nume_variabilă>[<Dimensiune>];
<Tip> declară tipul de bază al matricei, care este tipul fiecărui element al
său.
<Dimensiune> indică numărul maximal de elemente pe care le poate
conţine matricea.
Total_octeţi = sizeof(<Tip>)*<Dimensiune>
float sir[10];
49
float *p;
float sir[10];
p=sir;
<Tip> <Nume_variabilă>[<Dimensiune_1>][<Dimensiune_2>];
int matr[10][20];
50
Adresarea elementelor matricei se bazează pe premiza că, după fiecare
dimensiune, indexarea începe de la 0. De asemenea, facem precizarea că,
atunci când o matrice bidimensională este utilizată ca un argument pentru o
funcţie, se transmite doar un pointer către primul element al matricei. Însă,
parametrul care primeşte o matrice bidimensională trebuie să definească cel
puţin numărul de coloane, necesare compilatorului pentru a indexa corect
matricea. De urmărit exemplul de mai jos de cod C care permite calculul sumei
elementelor strict pozitive de pe diagonala principala a unei matrice patratice .
#include<stdio.h>
#include<conio.h>
51
//Apelare functie cu parametru matrice transmisa ca pointer static
sespdp(matr);
}
<Tip> <Nume_variabilă>[<Dim_1>][<Dim_2>]…[Dim_n];
#include<stdio.h>
#include<conio.h>
#include "facilcrt.h"
#include<stdlib.h>
//Directiva #define permite specificarea unor macrouri
#define lmaxs 100
#define optiuni "1234"
//Solutie modularizata C pentru problema determinrii
//ortogonalitatii a doi vectori reali
char prelopt();
void prelvec();
int ortogon();
float sir1[lmaxs],sir2[lmaxs];
int dims;
void main()
{
for(; ;)
{
switch (prelopt())
{
case '1':
{ prelvec();
break;
}
case '2':
{ ortogon();
break;
};
case '3': exit(0);
}
52
}
}
char prelopt()
{
char opt;
clrscr();
gotoxy(20,9);
cprintf("Optiunile programului....");
gotoxy(20,10);
cprintf("1- Preluare vectori");
gotoxy(20,11);
cprintf("2- Determinare ortogonalitate....");
gotoxy(20,12);
cprintf("3- Terminare program");
makewin2(20,9,55,12);
gotoxy(20,15);
cprintf("Optiunea Dvs.:");
makewin2(20,15,55,15);
do
{
gotoxy(20+strlen("Optiunea Dvs.:"),15);
opt=getch();
}
while (strchr(optiuni,opt)==NULL);
return opt;
}
void prelvec()
{
int i,col;
clrscr();
gotoxy(20,9);
cprintf("Dimensiune vectori:");
makewin2(20,9,20+strlen("Dimensiune vectori:"),9);
gotoxy(22+strlen("Dimensiune vectori:"),9);
col=strlen("Dimensiune vectori:");
makewin2(22+col,9,30+col,9);
gotoxy(23+col,9);
cscanf("%d",&dims);
clrscr();
gotoxy(20,9);
cprintf("Preluarea componentelor primului vector...");
makewin2(20,9,20+strlen("Preluarea componentelor primului vector..."),12);
for (i=0;i<dims;i++)
{
53
gotoxy(20,10);
cprintf("Elementul [ %d ]=",i);
cscanf("%f",&sir1[i]);
}
clrscr();
gotoxy(20,9);
cprintf("Preluarea componentelor celui de-al doilea vector...");
makewin2(20,9,20+strlen("Preluarea componentelor celui de-al doilea
vector..."),12);
for (i=0;i<dims;i++)
{
gotoxy(20,10);
cprintf("Elementul [ %d ]=",i);
cscanf("%f",&sir2[i]);
}
}
int ortogon()
{
float suma;
int i;
suma=0;
for (i=0;i<dims;i++)
suma=suma+sir1[i]*sir2[i];
clrscr();
if (suma)
{
gotoxy(20,10);
textcolor(RED+BLINK);
cprintf("Vectorii nu sunt ortogonali!!");
makewin2(20,10,21+strlen("Vectorii nu sunt ortogonali!!"),10);
getch();
textcolor(WHITE);
}
else
{
gotoxy(20,10);
textcolor(GREEN+BLINK);
cprintf("Vectorii sunt ortogonali!!");
makewin2(20,10,21+strlen("Vectorii sunt ortogonali!!"),10);
getch();
textcolor(WHITE);
};
}
54
Se cuvine să mai fac o paranteză referitoare la insistenţa cu care apar în
programele C/C++ construcţii sintactice care încep cu #, precum #include,
#define. Aceste construcţii fac parte din categoria instrucţiunilor care se
adresează compilatorului şi se mai numesc şi directive pentru preprocesor. deşi
nu fac parte din limbajul C/C++ aceste directive lărgesc sfera de acţiune a
programelor C/C++. Am văzut deja care este utilitatea directivei preprocesor
#include. Să spunem, totodată, faptul că, directiva pentru preprocesor #define
permite definirea unor macrouri astfel:
55
6 Pointeri
Un pointer este o variabilă care poate conţine o adresă de memorie. Această
adresă este localizarea în memorie a unui alt obiect (de regulă o altă variabilă).
De exemplu, dacă o variabilă conţine adresa alteia, despre prima se spune că
este un pointer la cea de-a doua. O variabilă de tip pointer se declară în C astfel:
<Tip> * <Nume>;
unde <Tip> este tipul de bază al pointerului iar <Nume> este numele variabilei
pointer. Tipul de bază al pointerului defineşte tipul de variabilă către care indică
pointerul. Practic, orice tip de pointer poate să indice orice în memorie.
Problema este, însă, că aritmetica pointerilor este integral raportată la tipul
de bază al pointerului.
Expresii cu pointeri
În general, expresiile care implică pointeri se conformează aceloraşi reguli ca şi
celelalte expresii. Există, totuşi, o serie de particularităţi pe care le vom evidenţia
în acest paragraf în ceea ce priveşte aritmetica pointerilor.
Astfel:
ÖAtribuirile sunt permise între pointeri concordanţi ca tip.
ÖPointerilor li se poate aplica operatorul ++ (incrementare) ca mai jos
:
int *p;
int mat[10];
:
p=&mat[0];
:
p++; // Operatorul de incrementare aplicat pointerului p
p- -;
:
În urma aplicării operatorului de incrementare pointerului p, care conţinea adresa
primului element al tabloului unidimensional mat, offset-ul acestuia este
incrementat cu 2, adică atât cât este lungimea în octeţi a tipului de bază al
pointerului p.
Ö Pointerilor li se poate aplica operatorul - - (decrementare) .
În urma aplicării operatorului de decrementare pointerului p, care conţinea
adresa celui de-al doilea element al tabloului unidimensional mat, offset-ul
acestuia este decrementat cu 2, adică atât cât este lungimea în octeţi a tipului de
bază al pointerului p.
56
ÖDe asemenea, putem aduna sau scădea întregi la, sau din pointeri. De
exemplu:
:
p=p+10;
:
face ca p să indice al 10-lea element de acelaşi tip cu tipul de bază al lui p,
relativ la elementul curent. În exemplul de mai jos ilustrăm utilitatea aritmeticii
pointerilor în contextul lucrului cu matrice.
#include<stdio.h>
#include<conio.h>
int matr[10][10]; //matrice de intregi bidimensionala
int *p; // pointer la intregi
int dimm;
void main()
{
int i,j,el;
clrscr();
gotoxy(20,10);cprintf("Dimensiune matrice :");
scanf("%d",&dimm);
clrscr();
for(i=0;i<dimm;i++)
for(j=0;j<dimm;j++)
{
gotoxy(20,12);
cprintf("Elementul[ %d , %d ]=",i,j);
scanf("%d",&el);
matr[i][j]=el;
};
p=&matr[0][0]; //p refera primul element al matricei
for (i=0;i<dimm;i++)
cprintf("\r\n Ref_poin=%d",*(p+i)); //modificare offset p in expresie
getch();
p=&matr[0][0];
for (i=0;i<dimm;i++)
{
cprintf("\r\n Ref_poin=%d",*(p));
p=p+1; //modificare offset p prin incrementare
}
getch();
p=&matr[0][0];
for (i=0;i<dimm;i++)
{
cprintf("\r\n Ref_poin=%d",*(p));
p++; //aplicare operator de incrementare
57
}
getch();
}
if (p<q) cprintf (“p indica o memorie de adresa mai mica decat q\n”);
Memoria alocată de funcţiile de alocare dinamică din C este obţinută din HEAP-
zona de memorie liberă situată între zona permanentă a memoriei programului şi
stivă.
Nucleul sistemului de alocare din C constă din funcţiile malloc() şi free().
Aceste “instrumente” de alocare lucrează în pereche, folosind zona de memorie
liberă pentru a stabili şi a păstra o listă cu memoria disponibilă.
Funcţia malloc() alocă memorie, având următorul prototip:
58
modul de utilizare a funcţiei memcpy, în situaţia în care se doreşte copierea
conţinutului memoriei de la o anumită adresă la altă adresă. De remarcat,
totodată, faptul că bibliotecile C au numeroase alte funcţii pentru a satisface
cerinţele de alocare/ manipulare dinamică a memoriei în programele utilizator.
#include<stdio.h>
#include<conio.h>
#include<stdlib.h>
#include<string.h>
void main()
{
char c[20];
int *p;
//pointer generic
void *x;
int nr=10000;
p=&nr;
clrscr();
gotoxy(20,10);
59
7 Structuri
O structură este, pragmatic vorbind, un grup de variabile reunite sub acelaşi
nume, ceea ce permite un mod convenabil de manipulare a unor date care au
afinităţi semantice între ele. O declarare de structură formează un şablon care
poate fi folosit pentru a crea structuri efective. În C, variabilele care fac parte din
structură se numesc membri ai structurii. Uzual, membrii structurii se mai
numesc şi elemente sau câmpuri. Aşa cum vom vedea mai jos, o structură
poate încapsula şi metode de prelucrare a datelor, dacă programatorul doreşte
să abstractizeze tipuri de date cu suport struct.
Sintaxa generală pentru declararea unei structuri este:
struct <Nume_generic>
{
<Tip> <Nume_membru_1>;
<Tip> <Nume_membru_2>;
:
<Tip> <Nume_membru_n>;
:
<Tip returnat> <Nume functie>(<Lista de parametri>);
} <Variabila_1>,[…<Variabila_k>];
În cazul în care în definiţia structurii este încapsulată şi o metodă atunci la
implementare se foloseşte sintaxa:
#include<stdio.h>
#include<conio.h>
typedef struct
{
char matricol[6];
char nume[30];
float media;
void setstud(char matr[],char num[],float med);
} TStud;
void TStud::setstud(char matr[],char num[],float med)
{
strcpy(matricol,matr);
strcpy(nume,num);
media=med;
60
};
void main()
{
clrscr();
TStud stud;
stud.setstud("12","Mihai Guramare",10);
printf("Matricol :%s\n",stud.matricol);
printf("Nume :%s\n",stud.nume);
printf("Media :%f",stud.media);
getch();
}
<Variabila_structurată> . <Nume_membru>
#include<conio.h>
#include<iostream.h>
#include<string.h>
void main()
{
typedef struct TPers{
char nume[30];
float salariu;
} TPers;
TPers pers;
strcpy(pers.nume,"Test typedef + struct….");
pers.salariu=1200000;
clrscr();
cout<<pers.nume<<"\r\n"<<pers.salariu;
getch();
}
#include<conio.h>
#include<iostream.h>
#include<string.h>
void main()
{
61
typedef struct TPers{
char nume[30];
float salariu;
} TPers;
TPers *pers;
typedef struct
{
tip1 nume1:lungime1;
tip2 nume2:lungime2;
...
tipn numen:lungimen;
} TOctet;
Tipurile pot lua una din valorile int, unsigned, signed, pentru majoritatea
compilatoarelor.
#include<stdio.h>
#include<conio.h>
#include<string.h>
typedef struct
{
unsigned bit :1;
unsigned :7;
unsigned :7;
unsigned bits:1;
} TOctet;
TOctet Octet;
62
int nr=1;
int *pointer;
void main()
{
int i;
pointer=&nr;
memcpy(&Octet,pointer,sizeof(int));
clrscr();
printf("Bitul 0 : %i\n",Octet.bit);
printf("Bitul de semn: %i\n",Octet.bits);
getch();
}
63
8 Uniuni
O uniune este o locaţie de memorie care este partajată în momente diferite între
două sau mai multe variabile diferite.
Sintaxa generala şi modul de utilizare pot fi deduse din exemplul de mai jos.
#include<stdio.h>
#include<conio.h>
#include<string.h>
typedef union
{
int codsal;
float salar;
} TUSal;
void main()
{
clrscr();
TUSal vunion;
vunion.codsal=10;
printf("Cod confidential salariu:%i\n",vunion.codsal);
vunion.salar=1000.50;
printf("Salariu :%f",vunion.salar);
getch();
}
64
II Programarea C++
65
1 Introducere
Programarea orientată pe obiecte (POO) este expresia, în materie de codificare
a proiectelor, a paradigmei care presupune modelarea orientată pe obiecte a
sistemelor soft.
POO este o paradigmă care câştigă tot mai mulţi aderenţi datorită calităţilor
pe care le au produsele şi sistemele soft realizate în spiritul conceptelor şi
principiilor promovate de aceasta. Rezultantă a unor direcţii diverse de
cercetare şi experimentare (programare structurată, programare modulară,
programare orientată pe structuri abstracte, reprezentarea cunoştinţelor în
sisteme expert, etc.) POO, aplicată corect poate rezolva, parţial sau
integral, multe din problemele obsedante ale ingineriei softului, în genere:
reutilizarea codului, extinderea/modificarea cu minim de efort a sistemelor
soft, ascunderea detaliilor de implementare faţă de anumite categorii de
utilizatori ai sistemelor soft. În acest mod platforma POO poate ajuta
programatorii şi companiile de soft să realizeze produse şi sisteme soft
performante, în timp util şi la un preţ scăzut.
1. 1 Concepte POO
Orice demers de modelare orientată pe obiecte apelează la o serie de concepte
specifice paradigmei POO.
Astfel, pentru modelarea unui sistem soft se operează frecvent cu:
1. Concepte ale teoriei generale a sistemelor (sistem, subsistem,
descompunere, agregare, structură, etc.)
2. Concepte care provin din arsenalul conceptual al modelării în genere
a sistemelor şi produselor soft (modul, modularizare, interfaţă, tip de dată,
structură de date, ascunderea informaţiei, etc.).
Tuturor acestora li se adaugă principiile cu ajutorul cărora aceste concepte devin
operaţionale.
Paradigma POO a intrat în competiţia pentru modernizarea şi îmbunătăţirea
reală a procesului de realizare a unui sistem soft cu un set propriu de concepte şi
principii.
Prezentăm mai întâi conceptele cheie ale paradigmei POO.
Este evident faptul că identificarea unei clase este în mod normal, rezultatul unui
demers cognitiv care presupune caracterizarea unui obiect prin însuşirile lui
(informaţionale şi comportamentale) care îi definesc apartenenţa la o anumită
clasă de obiecte. Aşadar, conceptul de clasă adună laolaltă datele şi metodele
de prelucrare a acestora.
66
Acesta este un exerciţiu de îndemânare a cărui rezolvare o poate învăţa oricine
care încearcă, are răbdare cu el însuşi şi citeşte cum procedează iniţiaţii când
întâlnesc astfel de exerciţii. Aşa cum va reieşi din implementarea conceptului de
clasă în C++, de exemplu, conceptul de clasă este o abstracţie care
pregăteşte un anumit tip de background pentru descrierea soluţiei unei
probleme date. Fără a intra prea mult în problemă să amintim, totuşi, că
modelând obiect orientat soluţiile problemelor noastre ne asigurăm o serie
de avantaje imediate şi de perspectivă în ceea ce priveşte gestiunea relaţiei
dintre domeniul problemei şi domeniul soluţiei în ingineria sistemelor soft.
67
Este evident faptul că obiectul care emite mesajul trebuie să cunoască protocolul
de comunicaţie al obiectului receptor.
De asemenea, se subânţelege faptul că mesajul trimis de obiectul emiţător va
provoca o reacţie(= un răspuns) din partea obiectului receptor.
1. 2 Principiile POO
Noutatea POO, ca paradigmă este ilustrată şi de principiile pe care le
promovează pentru a completa potenţialul oferit de concepte. Promovarea
sistematică a principiilor pe care le prezentăm în continuare promite, dar
nu garantează, realizarea unor produse, sisteme sau platforme soft
remarcabile din punct de vedere al performanţelor şi al efortului de
întreţinere.
68
Acest principiu este de mare utilitate în transmiterea sistematică a similarităţilor
de la o clasă la alta. Principiul poate fi aplicat cu succes doar în conjuncţie cu
operatorii conceptuali complementari:generalizarea şi specializarea.
Generalizarea apare în relaţia dintre o clasă şi una sau mai multe versiuni mai
rafinate ale acesteia. Este, în limbajul de specialitate, relaţia dintre clasa de
bază (sau superclasă) şi clasele derivate (sau subclase).
Atributele şi operaţiile comune sunt grupate în superclasă şi se spune că sunt
moştenite de subclase.
Se poate spune că moştenirea se referă la mecanismul de a transmite atribute şi
operaţii de-a lungul unei relaţii de generalizare. Evident, simpla moştenire a
proprietăţilor unei clase nu rezolvă nici o problemă din punct de vedere
conceptual. Utilitatea principiului moştenirii este reliefată în condiţiile aplicării
operatorului de specializare, prin care subclasa rafinează (specializează)
superclasa.
Implementarea moştenirii nu creează probleme deosebite de înţelegere şi
utilizare.
69
2 Implementarea C++ a POO
2.1 Definirea claselor
Pentru a putea lucra cu obiecte în C++ trebuie, în prealabil, să definim forma lor
generală folosind în acest scop cuvântul cheie class. O clasă este similară,
sintactic vorbind, cu o structură. Forma generală a unei declaraţii de clasă care
nu moşteneşte nici o altă clasă este următoarea:
Pentru datele şi funcţiile care fac parte din definiţia unei clase se
obişnuiesc şi denumirile de variabile membre, respectiv, funcţii membre
sau, pur şi simplu, membri.
70
#include <conio.h>
#include <string.h>
#include <iostream.h>
pers.setfields("Radu Vasile",1,10000);
pers.afis();
}
71
anumitor categorii de utilizatori accesul la membrii clasei, definind pentru
comunicaţia cu alte clase şi categorii de utilizatori ceea ce se numeşte interfaţa
clasei.
Evident, se poate face o oarecare analogie între clasă şi structură; analogia
cade, însă, în momentul în care se pune problema manevrabilităţii instanţelor
celor două tipuri de concepte sau atunci când este vorba de moştenire şi
polimorfism. Un exemplu ceva mai apropiat de realitatea preocupărilor unui
programator C++ poate fi programul care implementează o stivă de întregi,
prezentat în continuare.
#include <iostream.h>
#include <conio.h>
#define SIZE 100
72
int stack::pop()
{
if(top==0)
{
gotoxy(20,24);
cout<<"Depasire inferioara stiva!!!";
getch();
return 0;
};
top--;
return st[top];
}
//Functia principala
void main()
{
int i;
stack st1,st2;
st1.init();
st2.init();
for (i=0;i<=9;i++)
{
st1.push(i);
st2.push(9-i);
}
clrscr();
for (i=0;i<=9;i++)
{
gotoxy(35,wherey()+1);
cout<<st1.pop()<<"*****";
cout<<st2.pop()<<endl;
}
getch();
}
73
Modificatorii de protecţie utilizaţi în <Lista_claselor_de_bază> definesc protecţia
în clasa derivată a elementelor moştenite. Prezentăm sub formă tabelară accesul
la elementele moştenite de clasa derivată în funcţie de protecţia fiecărui element
şi de modificatorul de protecţie asociat în <Lista_claselor_de_bază>.
Exemplu
#include <iostream.h>
class CB
{
protected:
int i,j;
//Pentru un program care utilizează această clasă
//sau pentru altă clasă care nu se află în relaţie
//de derivare cu CB I,j sunt private din punct de
//vedere al accesului. i,j sunt accesibile unei clase derivate.
public:
void setij(int a, int b) {i=a;j=b}
void dispij() { cout<<i<<” “<<j<<endl;}
}
class CD:public CB
{
int k;
public:
void setk(){k=I*j;}
void dispk(){cout<<k<<endl;}
74
}
void main()
{
CD ob;
ob.setij(2,3); //OK!
ob.dispij(); //OK
ob.setk();
ob.dispk ();
}
Exemplu
#include <iostream.h>
class CB
{
protected:
int i,j;
//Pentru un program care utilizează această clasă
//sau pentru altă clasă care nu se află în relaţie
//de derivare cu CB i, j sunt private din punct de
//vedere al accesului. i,j sunt accesibile unei clase derivate.
public:
void setij(int a, int b) {i=a;j=b}
void dispij() { cout<<i<<” “<<j<<endl;}
}
class CD:protected CB
{
int k;
public:
void setk(){setij(10,20);k=I*j;}
void disptot(){cout<<k<<” “;dispij();}
}
void main()
{
CD ob;
ob.setij(2,3); //ilegal!
ob.setk(); //OK!, membru public în CD
75
ob.setk();
ob.disptot (); //OK!
ob.dispij(); //ilegal! Membru protected în CD
}
Constructori şi destructori
Este un fapt obişnuit necesitatea ca unele elemente ale unui obiect să fie
iniţializate. Pentru a degreva programatorul de o asemenea sarcină de rutină,
compilatorul C++ generează cod care permite obiectelor să se iniţializeze
singure. Această iniţializare automată este efectuată prin intermediul unei funcţii
membru speciale a clasei definitoare numită constructor.
76
Foarte important mi se pare să semnalez şi existenţa unui constructor
special, numit constructor de copiere. Acesta are rolul de a atribui datele unui
obiect altuia. Mai mult, poate fi apelat chiar la definirea obiectelor. Dacă
programatorul nu defineşte propriul constructor de copiere, compilatorul adaugă
automat un astfel de constructor. În exemplul de mai jos se pot urmări elemente
de sintaxă şi semantică care trebuie cunoscute când se lucrează cu constructori
în diferite ipostaze.
#include<stdio.h>
#include<conio.h>
//Definire clasă
class intreg
{
public:
int a;
//Constructor parametrizat
intreg(int v)
{
printf("Constructor parametrizat\n");
getch();
a=v;
}
//Constructor de copiere
intreg (intreg& v)
{
a=v.a;
printf("Constructor de copiere\n");
getch();
}
void dispa()
{
printf("a=%i\n",a);
getch();
};
};
void main()
{
clrscr();
//Declarare obiect static cu iniţializare;
intreg x=100;
x.dispa();
//Declarare obiect static cu iniţializare prin copiere
intreg y=x;
77
y.dispa();
}
Evident, o clasă poate avea mai mulţi constructori, ceea ce este tot în beneficiul
programatorilor.
<Pointer>=new <Tip>;
delete <Pointer> ;
78
<Pointer>=new <Tip> (<Valoare_iniţială>);
ca în exemplul:
int *p;
p=new int(10);
delete [ ] <Pointer>
<Pointer_la_obiect>=new <Clasa>;
<Pointer_la_obiect>=new <Clasa>(<Lista_valori>);
#include <iostream.h>
#include <conio.h>
#define SIZE 100
79
// Definirea clasei stack
class stack
{
int st[SIZE];
int top;
public:
stack(); //constructor
~stack(); //destructor
void push(int i);
int pop();
};
//Implementare constructor
stack::stack()
{
top=0;
gotoxy(20,24);
cout<<"Stiva este initializata";
getch();
clrscr();
}
stack::~stack()
{
gotoxy(20,24);
cout<<"Stiva este distrusa!!";
getch();
clrscr();
}
80
{
if(top==0)
{
gotoxy(20,24);
cout<<"Depasire inferioara stiva!!!";
getch();
return 0;
};
top--;
return st[top];
}
//Functia principala
void main()
{
int i;
stack st1,st2;
for (i=0;i<=9;i++)
{
st1.push(i);
st2.push(9-i);
}
clrscr();
for (i=0;i<=9;i++)
{
gotoxy(35,wherey()+1);
cout<<st1.pop()<<"*****";
cout<<st2.pop()<<endl;
}
getch();
}
Funcţii virtuale
O funcţie virtuală este o funcţie declarată virtual în clasa de bază şi redefinită
într-un lanţ de derivare asociat respectivei clase de bază. O funcţie virtuală
defineşte o clasă generală de acţiuni. O redefinire a ei introduce o metodă
specifică. În esenţă, o funcţie virtuală declarată în clasa de bază acţionează ca
un substitut pentru păstrarea elementelor care specifică o clasă generală de
acţiuni, stabilind elementele de interfaţă. Redefinirea unei funcţii virtuale într-o
81
clasă derivată oferă operaţiile efective pe care le execută funcţia. Utilizate static,
funcţiile virtuale se comportă ca oricare altă funcţie membru a clasei .
Capabilităţile funcţiilor virtuale ies în evidenţă atunci când sunt apelate în context
dinamic.
Exemplu
#include <iostream.h>
#include <conio.h>
//Clasa de baza care are doua functii membri publici
//o functie ordinara
//cealalta functie virtuala
class CB
{
public:
void f()
{
cout<<"CB::f()"<<endl;
getch();
}
virtual void g() //funcţie virtuală
{
cout<<"CB::g()"<<endl;
getch();
}
};
82
void ExecPolim(CB *p);
void main()
{
CB *pb=new CB;
CD *pd=new CD;
clrscr();
pb->f();
pb->g();
pd->f();
pd->g();
clrscr();
cout<<"Apel polimorfic in context CB"<<endl;
ExecPolim(pb);
cout<<"Apel polimorfic in context CD"<<endl;
ExecPolim(pd);
delete pb;
delete pd;
};
La prima vedere redefinirea unei funcţii virtuale într-o clasă derivată pare similară
cu supraîncărcarea unei funcţii. Nu este aşa, deoarece există mai multe
diferenţe:
@Prototipul pentru o funcţie virtuală redefinită trebuie să coincidă cu
prototipul specificat în clasa de bază.
@Funcţiile virtuale nu pot să fie membri de tip static ai clasei din care fac
parte şi nu pot fi nici friend
Funcţiile obişnuite suportă supraîncărcarea; funcţiile virtuale suportă
suprascrierea.
În cele ce urmează facem o serie de precizări relativ la clauza virtual.
83
Deoarece în C++ moştenirea este ierarhizată este normal ca funcţiile virtuale să
fie, de asemenea, ierarhizate. Acestea înseamnă că, atunci când o clasă
derivată nu suprascrie o funcţie virtuală, este utilizată prima redefinire găsită în
ordinea inversă derivării.
O clasă care conţine cel puţin o funcţie virtuală pură se numeşte clasă
abstractă. O clasă abstractă nu poate fi utilizată pentru crearea de obiecte dar
poate fi utilizată pentru a crea pointeri şi referinţe spre astfel de clase permiţând
astfel manifestările polimorfice în timpul execuţiei.
#include <iostream.h>
#include <conio.h>
//functia abs este definita in trei moduri diferite
int abs(int i);
double abs(double d);
long abs(long l);
void main()
{
clrscr();
84
cout<<abs(-10);
cout<<abs(-10.0);
cout<<abs(-10L);
getch();
}
int abs(int i)
{
gotoxy(20,10);cout<<"Varianta int !!! :";
return i<0 ?-i:i;
}
double abs(double d)
{
gotoxy(20,12);cout<<"Varianta double!!! :";
return d<0 ?-d:d;
}
long abs(long l)
{
gotoxy(20,14);cout<<"Varianta long !!! :";
return l<0 ?-l:l;
}
85
Este posibil să se permită unei funcţii care nu este membru al unei clase să aibă
acces la membrii privaţi ai clasei declarând-o ca funcţie prietenă a clasei (friend).
Aşadar, o funcţie prietenă are acces la membrii private şi protected ai clasei
căreia îi este prietenă. Sintaxa pentru declararea unei funcţii prietene este:
#include<conio.h>
#include <iostream.h>
#define liber 0
#define ocupat 1
class Clasa2
{
//<ocupat> daca o metoda a clasei a scris pe ecran
//<liber> in caz contrar
int stare;
public:
void setare_stare(int val);
friend int ecran_liber(Clasa1 a,Clasa2 b);
};
86
int ecran_liber(Clasa1 a, Clasa2 b)
{
if (a.stare ||b.stare)
return 0;
else
return 1;
}
void main()
{
Clasa1 x;
Clasa2 y;
x.setare_stare(ocupat);
y.setare_stare(liber);
if (ecran_liber(x,y))
{
clrscr();
gotoxy(20,12);
cout<<"Ecranul este liber...";
getch();
}
else
{
clrscr();
gotoxy(20,12);
cout<<"Ecranul este ocupat...";
getch();
};
x.setare_stare(liber);
y.setare_stare(liber);
if (ecran_liber(x,y))
{
clrscr();
gotoxy(20,12);
cout<<"Ecranul este liber...";
getch();
}
else
{
clrscr();
gotoxy(20,12);
cout<<"Ecranul este ocupat...";
getch();
};
}
87
2.7 Supraîncărcarea operatorilor
Polimorfismul este realizat în C++ şi prin supraîncărcarea operatorilor. După
cum s-a văzut în numeroase exemple, în C++ se pot folosi operatorii >> şi <<
pentru a efectua operaţii I/O relativ la consolă. Aceşti operatori pot efectua
aceste operaţii suplimentare (ştiut fiind faptul că pot funcţiona şi ca operatorii de
shiftare la nivel de biţi) deoarece operaţiile sunt supraîncărcate în fişierul antet
IOSTREAM.H. Când un operator este supraîncărcat, el capătă o semnificaţie
suplimentară relativ la o anumită clasă fără să-şi piardă vreunul din înţelesurile
iniţiale. Majoritatea operatorilor din C++ pot fi supraîncărcaţi, stabilind
semnificaţia lor relativ la o anumită clasă.
Limbajul C++ permite supraîncărcarea numai a operatorilor existenţi în limbaj.
Dintre aceştia nu pot fi supraîncărcaţi operatorii: . :: ? : .
Să mai precizăm faptul că, prin supraîncărcarea operatorilor nu se poate
schimba n-aritatea, prioritatea sau asociativitatea operatorilor, acestea fiind
elemente predefinite pentru tipuri predefinite şi deci ele se vor menţine şi pentru
tipuri abstracte.
Prin n-aritate înţelegem că operatorul este unar sau binar.
Supraîncărcarea operatorilor se realizează cu ajutorul unor funcţii membre sau
prietene speciale. Specificul acestora se află în numele lor. El se compune din
cuvântul cheie operator şi unul sau mai multe caractere care definesc operatorul
care se supraîncarcă. Între cuvăntul cheie operator şi caracterele care definesc
operatorul care se supraîncarcă se află cel puţin un spaţiu alb. Felul în care sunt
scrise funcţiile operator diferă pentru cele de tip membru de cele de tip friend.
//Operaţii specifice
# este o notaţie pentru numele operatorului aşa cum va fi folosit in program după
redefinire. Deci, dacă supraîncărcăm operatorul = atunci sintaxa prototipului
funcţiei membru va fi:
Funcţiile operator membre au un singur parametru sau nici unul. În cazul în care
au un parametru, acesta se referă la operandul din dreapta al operatorului.
Celălalt operand ajunge la operator prin intermediul pointerului special this.
88
Astfel stând lucrurile, trebuie să avem grijă de eventualitatea ca operandul din
stânga să fie o constantă, ceea ce înseamnă ca nu mai avem context de apel
pentru operatorul redefinit.
Inivităm cititorul să urmărească exemplul de mai jos.
#include<conio.h>
#include<iostream.h>
//prototipul operatorului +
complex operator+(complex &op2);
};
void complex::disp_nc()
{
cout<<x<<"+i*"<<y;
};
//Implementare operator +
//Aceasta sintaxa transforma apelul <ob1+ob2>
//in +(ob2) ob1 fiind accesibil prin pointerul
//special <this>
complex complex::operator+(complex &op2)
{
complex temp;
temp.x=op2.x+x;
temp.y=op2.y+y;
return temp;
89
};
void main()
{
complex tamp1,tamp2;
complex *pod1,*pod2;
complex ob1(10,10),ob2(11,11);
clrscr();
gotoxy(20,10);cout<<"Primul numar complex ->:";
ob1.disp_nc();
getch();
gotoxy(20,11);cout<<"Al doilea numar complex->:";
ob2.disp_nc();
getch();
ob1=ob1+ob2;
gotoxy(20,13);cout<<"Suma numerelor complexe->:";
ob1.disp_nc();
getch();
pod1=new complex(200,200);
pod2=new complex(300,300);
tamp1=*pod1;
clrscr();
gotoxy(20,10);cout<<"Al treilea numar complex ->:";
tamp1.disp_nc();
tamp2=*pod2;
gotoxy(20,11);cout<<"Al patrulea numar complex ->:";
tamp2.disp_nc();
90
Invităm cititorul să urmărească utilizarea funcţiilor friend la supraîncărcare în
Exerciţiul 6 de la Capitolul aplicativ al prezentului suport de curs.
#include…
declaraţii clase de bază
declaraţii clase derivate
prototipuri de funcţii nemembre
main()
{
:
}
definiţii de funcţii ne-membre
91
3 Bazele sistemului I/O în C++
Pe lângă faptul că permite sistemul de I/O din C, C++ defineşte propriul său
sistem I/O orientat pe obiecte. Ca şi sistemul de I/O din C, cel din C++ este
complet integrat. Aceasta înseamnă că diferitele tipuri de operaţii I/O sunt doar
perspective diferite ale aceluiaşi mecanism. Această perspectivă integratoare
asupra operaţiilor I/O are la bază, atât în C cât şi în C++, conceptul de flux
(stream).
Fluxuri binare
Un flux binar este o secvenţă de octeţi într-o corespondenţă biunivocă cu cei de
la echipamentul extern.
Fişiere
În C/C++ un fişier poate să fie: un fişier de pe disc, tastatura, ecranul monitorului,
imprimanta,etc. Un flux se asociază cu un anumit fişier efectuând o operaţie de
deschidere. Odată deschis fişierul, este posibil schimbul de date între el şi
programul utilizator care l-a deschis.
De observat faptul, trivial pentru cunoscători, că nu toate fişierele au aceleaşi
posibilităţi. De exemplu, un fişier de pe disc poate să admită un acces aleator la
datele stocate în el, în timp ce imprimanta nu o poate face. Astfel că, putem
concluziona, pentru claritate:
92
Pentru sistemul I/O din C/C++ toate fluxurile sunt la fel dar nu şi
fişierele.
Fluxurile cin, cout, cerr corespund fluxurilor stdin, stdout, stderr din C. Implicit,
fluxurile standard sunt folosite pentru a comunica cu consola. Însă, în mediile
care admit redirecţionarea I/O, fluxurile standard pot fi redirecţionate spre alte
echipamente sau fişiere.
93
I/O formatate în C++
Sistemul de I/O din C++ vă permite să formataţi operaţiile I/O, aşa cum se
întâmpla şi în cazul utilizării funcţiilor C pentru operaţii I/O, precum: printf,
cprintf, scanf,etc. De exemplu, se poate specifica mărimea unui câmp, baza
unui număr, numărul de cifre după punctul zecimal,etc. Operatorii din C++
utilizaţi pentru introducerea informaţiilor de formatare sunt >> şi <<.
Există două căi înrudite, dar conceptual diferite, prin care se pot formata datele.
În primul rând, putem avea acces direct la diferiţi membri ai clasei ios. În al
doilea rând, putem folosi funcţii speciale numite manipulatori, care pot fi incluse
în expresii de I/O.
Prezentăm, în continuare modul de utilizare a manipulatorilor de formate, datorită
accesibilităţii mai mari a acestora.
Manipulatorii standard sunt prezentaţi în tabelul de mai jos.
94
argumentul întreg de tip
long
setprecision(int p) cout<<setprecision(6); Stabileşte precizia
cin>>setprecision(10); conversiei în virgulă
mobilă la numărul
specificat de zecimale
setw(int w) cout<<setw(6)<<var; Stabileşte mărimea unui
cin>>setw(24)>>buf câmp la numărul specificat
de caractere
ws cin ws; Elimină spaţiile libere din
fluxul de intrare
Tabelul 16. Manipulatori de formatare a operaţiilor I/O în C++
#include<iostream.h>
#include<iomanip.h>
#include<conio.h>
#include<stdio.h>
void main()
{
95
double nr;
clrscr();
gotoxy(10,6);
nr=7./3.;
gotoxy(5,6);
cout<<"Afisare numar in virgula mobila/ format implicit...";
gotoxy(10,7);
cout<<nr;
gotoxy(5,9);
cout<<"Afisare numar in virgula mobila/ cu precizia specificata...";
gotoxy(10,10);
cout<<setprecision(10)<<nr;
gotoxy(5,12);
cout<<"Afisare numar in virgula mobila/ format virgula fixa...";
gotoxy(10,13);
cout.setf(ios::fixed);
cout<<setprecision(10)<<nr;
gotoxy(5,15);
cout<<"Afisare numar in virgula mobila/ format virgula fixa...";
gotoxy(10,16);
cout.setf(ios::scientific);
cout<<setprecision(10)<<nr;
gotoxy(5,18);
cout<<"Afisare numar in virgula mobila/ format virgula fixa...";
gotoxy(10,19);
cout.setf(ios::scientific|ios::showpos);
cout<<setprecision(10)<<nr;
getch();
}
96
fluxuri: de intrare, de ieşire şi de intrare/ieşire. Pentru a crea un flux de intrare,
trebuie să-l declaraţi ca fiind din clasa ifstream. Pentru a crea un flux de ieşire,
trebuie să-l declaraţi ca fiind din clasa ofstream. Fluxurile care efectuiază atât
operaţii de intrare cât şi operaţii de ieşire, trebuie declarate ca fiind din clasa
fstream. Odată declarat fluxul, o modalitate de a-i asocia un fişier extern o
reprezintă utilizarea funcţiei open() având prototipul:
Puteţi specifica mai mult de un mod de lucru pentru un fişier, folosind operatorul
pe biţi SAU cu modurile respective. De exemplu, pentru deschiderea unui fişier
pentru ieşire şi poziţionarea pointerului la sfârşitul lui se folosesc modurile
ios::out şi ios::ate astfel:
97
ÖScrierea şi citirea fişierelor text
Sunt două operaţii foarte uşoare, realizate apelând la operatorii >> şi << într-un
mod asemănător operaţiilor referitoare la consola sistemului, cu deosebirea că în
loc să folosiţi cin şi cout apelaţi la un flux legat de un fişier . Codul de mai jos
arată cum poate fi afişat pe ecranul monitorului conţinutul unui fişier text.
#include <fstream.h>
#include <stdlib.h>
#include<conio.h>
char c;
clrscr();
while (in.get(c))
{
if (wherey()>20)
{
gotoxy(20,24);
cout<<"Press any key to continue...";
getch();
clrscr();
}
cout<<c;
}
in.close();
}
98
Există două modalităţi de a scrie şi citi date binare într-un fişier. Prima
modalitate se referă la utilizarea funcţiilor get() şi put(). Aceste funcţii sunt
orientate pe octeţi, ceea ce înseamnă că get() va citi un octet de date iar put() va
scrie un octet de date.
Funcţia get() are mai multe forme; o prezentăm, în continuare, împreună cu
omoloaga ei put(), pe cea mai des folosită:
#include<stdlib.h>
#include <fstream.h>
void main(int argc, char *argv)
{
//Verific daca sunt suficiente argumente
if(argc<3)
{
cerr<<"Mod de utilizare : filecopy <fisier sursa> <fisier destinatie>\n";
exit(0);
}
//Deschide fisierul de intrare si il conecteaza la fluxul ins
ifstream ins(argv[1]);
if(!ins)
{
cerr<<"Nu pot deschide fisierul sursa:"<<argv[1];
exit(1);
}
//Deschide fisierul de iesire si il conecteaza la fluxul outs
ofstream outs(argv[2]);
if(!outs)
{
cerr<<"Nu pot deschide fisierul sursa:"<<argv[2];
exit(1);
}
//Citeste din sursa si scrie in destinatie
char c;
while(ins.get(c) && outs) outs.put(c);
99
}
A doua modalitate de a citi şi scrie blocuri de date în binar este folosirea funcţiilor
din C++ read() şi write(). Prototipurile lor sunt:
Funcţia read() citeşte numar octeţi din fluxul asociat şi îl pune în buffer-ul indicat
de buf . Funcţia write() scrie în fluxul asociat numar octeţi citiţi din buffer-ul spre
care indică buf.
ÖDetectarea EOF
Puteţi să detectaţi sfârşitul fişierului folosind funcţia membru eof() ,care are acest
prototip:
int eof();
#include <fstream.h>
#include <ctype.h>
#include <iomanip.h>
#include <stdlib.h>
#include<conio.h>
void main(int argc,char *argv[])
{
if (argc!=2)
{
cerr<<"Mod de utilizare : lishex <nume fisier CPP>\n";
exit(0);
}
ifstream in(argv[1],ios::in|ios::binary);
if (!in)
{
cerr<<"Fisierul nu poate fi deschis!";
exit(0);
}
register int i,j;
int count=0;
char c[16];
cout.setf(ios::uppercase);
clrscr();
100
while(!in.eof())
{
for(i=0;i<16 && !in.eof();i++)
{
in.get(c[i]);
}
if (i<16) i--;
for(j=0;j<i;j++)
cout<<setw(3)<<hex<<(int) c[j];
for(;j<16;j++)
cout<<"\t";
for(j=0;j<i;j++)
if(isprint(c[j])) cout<<c[j];
else cout<<".";
cout<<endl;
count++;
if(count==16)
{
count=0;
cout<<"Press ENTER to continue!";
getch();
clrscr();
cout<<endl;
}
}
in.close();
}
streamoff este un tip definit în IOSTREAM.H, capabil să conţină cea mai mare
valoare validă pe care o poate avea offset, iar seek_dir este o enumerare care
are aceste valori:
ios::beg
ios::cur
ios::end
Sistemul de I/O din C++ operează cu doi pointeri asociaţi unui fişier. Unul este
pointerul de get , care specifică unde va apărea următoarea operaţie de intrare
în fişier. Celălalt este pointerul de put şi specifică unde va avea loc următoarea
101
operaţie de ieşire. După fiecare operaţie de intrare sau ieşire, pointerul
corespunzător este avansat automat, secvenţial. Dar, folosirea funcţiilor seekg()
şi seekp() permite un acces nesecvenţial la datele din fişier.
seekg() şi seekp() deplasează pointerul de înregistrare cu offset octeţi faţă de
origine.
102
3 Programare generică în C++
În programarea profesională apar nenumărate situaţii în care reutilizarea codului
presupune o soluţie de un anumit tip pentru o problemă dată. Situaţia la care ne
referim în această secţiune este, potenţial vorbind, următoarea: Ce putem face
pentru a comprima codul sursă în situaţia în care structuri de date, diferite
ca tip, suportă prelucrări similare.
Soluţia acestei probleme de stil de programare o reprezintă programarea
generică. Exprimându-ne în termenii limbajului C, o funcţie generică defineşte
un set general de operaţii care vor fi aplicate unor tipuri de date diferite.
Ca un exemplu, o soluţie generică pentru modelarea unei stive este un pretext
ideal pentru precizarea ideilor principale ale programării generice.
Altfel spus, dacă dorim o stivă, în care, de la caz la caz, să putem păstra numere
întregi, numere reale sau şiruri de caractere (deci tipuri de date diferite),
operaţiile fiind aceleaşi ( push() şi pop() ), este clar că ne aflăm în situaţia în
care avem nevoie de suport pentru scrierea de cod cu proprietăţi generice.
Dacă în Pascal programarea generică se baza pe tipuri procedurale şi
programarea la nivel de octet, în C++ există suport evoluat pentru programare
generică, sub forma şabloanelor. Cu un şablon, în C++ se poate crea o funcţie
generică sau o clasă generică.
ÖFuncţii TEMPLATE
O funcţie template este o funcţie şablon, având unul sau mai mulţi parametri
formali de un tip generic. În funcţie de nevoile de utilizare a acestei funcţii,
compilatorul generează funcţii propriu-zise, înlocuind tipul generic cu un tip
concret. Tipul concret poate fi orice tip fundamental, derivat sau clasă predefinită.
Considerăm un exemplu. Fie funcţia max(x,y) care returnează valoarea maximă
a argumentelor sale. Tipul variabilelor x şi y trebuie, obligatoriu, specificat în
momentul compilării. Soluţia clasică constă în redefinirea (over-loading) funcţiei
max pentru fiecare tip al argumentelor x şi y (de observat şi cele spuse la
paragraful 2.2 relativ la funcţii supraîncărcate în C++).
Trebuie, aşadar, să definim mai multe versiuni ale funcţiei max.
103
Mecanismul template permite definirea o singură dată a şablonului de funcţii,
după care se generează automat funcţiile propriu-zise în concordanţă cu
necesităţile de utilizare, dar ,evident, în faza de compilare.
De precizat următoarele:
@Caracterele < şi > fac parte din sintaxa obligatorie.
@Lista de parametri formali ai unei funcţii şablon trebuie să utilizeze toate
tipurile de date generice.
@În cazul funcţiilor template nu se fac conversii
@Funcţia care are acelaşi nume şi acelaşi număr de parametri cu o funcţie
şablon se numeşte caz exceptat (Supraîncărcarea explicită este prioritară).
#include <conio.h>
#include<iostream.h>
104
clrscr();
i=10;
j=2;
k=13;
l=-7;
#include <conio.h>
#include <stdio.h>
#include <string.h>
#include <iostream.h>
#include <ctype.h>
void main()
{
105
clrscr();
int i=0;
//***************************************
//Citire persoane de la tastatura
do
{
gotoxy(20,12);
cout<<"Numele persoanei:";clreol();
cin>>pers[i].np;
gotoxy(20,13);
cout<<"Matricola:";clreol();
cin>>pers[i].key;
gotoxy(20,14);
cout<<"Mai aveti(D,N):";
i++;
}
//***************************************
//Listare persoane pe ecranul monitorului
//in ordinea citirii de la tastatura
while(toupper(getch())!='N');
clrscr();
cout<<"Listare persoane in ordinea citirii de la tastatura..."<<endl;
cout<<"_______________________________________________"<<endl;
for(int j=0;j<i;j++)
{
if (wherey()>10)
{
cout<<"______________________________________________"<<endl;
cout<<"Pentru continuare apasati o tasta..."<<endl;
getch();
clrscr();
cout<<"Listare persoane in ordinea citirii de la tastatura..."<<endl;
cout<<"_______________________________________________"<<endl;
};
cout.width(30);cout.setf(ios::left);
cout<<pers[j].np<<" "<<pers[j].key<<endl;
};
getch();
//*************************************
//Sortare persoane
int sortat;
do
{
sortat=1;
106
for(int j=0;j<i-1;j++)
{
switch(comp(pers[j],pers[j+1]))
{
case 1: {
tam=pers[j];
pers[j]=pers[j+1];
pers[j+1]=tam;
sortat=0;
};
};
};
}
while(!sortat);
//****************************************
//Listare persoane dupa sortare in ordinea
//crescatoare a matricolelor
clrscr();
cout<<"Listare persoane dupa sortare........................."<<endl;
cout<<"____________________________________________"<<endl;
for(int k=0;k<i;k++)
{
if (wherey()>10)
{
cout<<"_____________________________________________"<<endl;
cout<<"Pentru continuare apasati o tasta..."<<endl;
getch();
clrscr();
cout<<"Listare persoane dupa sortare........................."<<endl;
cout<<"______________________________________________"<<endl;
};
cout.width(30);cout.setf(ios::left);
cout<<pers[k].np<<" "<<pers[k].key<<endl;
};
getch();
}
ÖClase TEMPLATE
O clasă template defineşte un şablon pe baza căruia se pot genera clase
propriu-zise. Din acest motiv, o clasă template se mai numeşte şi clasă generică
, clasă generator sau metaclasă.
Astfel că, o clasă template devine o clasă de clase, reprezentând cel mai
înalt nivel de abstractizare admis de programarea obiect orientată.
107
În cadrul clasei şablon se pot declara atribute informaţionale de un tip ambiguu,
care sunt particularizate în cadrul clasei generată pe baza şablonului. Evident, şi
în acest caz, generarea se face în faza de compilare în concordanţă cu cerinţele
clientului.
Sintaxa la specificarea clasei:
#include <iostream.h>
#include <stdlib.h>
#include <conio.h>
//***************************************
//Definire clasa matrice generica
template <class ATip>
class genmat
{
ATip a[SIZE];
public:
genmat();
ATip &operator [ ](int i); //Supraîncărcare operator [ ]
};
//***************************************
//Implementare constructor clasa generica
template <class ATip> genmat<ATip>::genmat()
{
register int i;
for(i=0;i<SIZE;i++)
a[i]=i;
};
108
//***************************************
//Implementare supraincarcare operator [ ]
template <class ATip> ATip &genmat<ATip>::operator[ ](int i)
{
if(i<0 ||i>SIZE-1)
{
cerr<<"Valoare indice eronata...";
getch();
exit(1);
};
return a[i];
};
//***************************************
//Functia principala
void main()
{
genmat<int> intob;
genmat<double> doubob;
int i;
clrscr();
cout<<"Matrice de intregi..."<<endl;
for(i=0;i<SIZE;i++)
intob[i]=i;
for(i=0;i<SIZE;i++)
cout<<intob[i]<<endl;
getch();
clrscr();
cout<<"Matrice de reali dubla precizie..."<<endl;
for(i=0;i<SIZE;i++)
doubob[i]=(double)i/3;
for(i=0;i<SIZE;i++)
cout<<doubob[i]<<endl;
getch();
clrscr();
intob[100]=100;
};
109
class Cstack
{
T * v; //pointer la varful stivei
T * p; //pointer la pozitia curenta din stiva
int dim; //dimensiunea stivei
public:
Cstack(int sz)
{
v=p=new T [dim=sz]; //alocare dinamica de memorie pentru p şi v
}
~Cstack()
{
delete [ ] v;
}
void push(T a)
{
*p++=a;
};
T pop()
{
return *-p
}
}
CStack<char> sc(100);
#include <iostream.h>
#include <conio.h>
110
T *top;
int dims;
public:
CStack( int sz) //constructor
{
v=top=new T[dims=sz];
}
~CStack() //destructor
{
delete [ ] v;
}
T pop()
{
return *--top; //Notatie prescurtata echibvalenta cu - -top; return *top
}
};
//Functia principala
void main()
{
int i;
//Primul exemplu de instantiere a stivei generice ;numere întregi
CStack<int> st1(20);
//Încarcare stiva
for (i=0;i<=9;i++)
{
st1.push(i);
}
111
//Al doilea exemplu de instantiere a stivei generice; caractere, începand cu “A”
CStack<char> st2(20);
//Încarcare stiva
for (i=65;i<75;i++)
{
st2.push((char) i);
}
112
4 Tratarea excepţiilor
Programatorii adevăraţi trebuie să ia în calcul şi posibilitatea de a crea programe
robuste, care fac faţă atât cerinţelor specificate dar nerafinate suficient, cât şi
cerinţelor nespecificate dar formulate de utilizator, din diverse motive.
Programele care au aceste calităţi se numesc robuste.
În programarea clasică soluţia acestei probleme se putea numi, destul de exact
spus, programare defensivă. Seamănă puţin cu conducerea preventivă din
şoferie dacă ne gândim că programând defensiv, în fond punem răul înainte,
deci nu ne bazăm pe cumsecădenia şi buna pregătire a utilizatorului.
Pentru a face faţă cerinţelor legate de problema tratării excepţiilor (aşa se
numesc în jargon profesional erorile care apar în timpul execuţiei programelor)
anumite limbaje de programare oferă suport adecvat. Aş include aici limbaje
precum Delphi, C++, Java, Visual C.
Nu toate compilatoarele de C++ oferă suport, dar standardul ANSI C++ cere
acest lucru în mod explicit. Compilatoarele din familia Borland incepând cu
versiunea 4.0 oferă acest suport.
Esenţialul din punctul de vedere al programatorului C++ este ca el să-şi formeze
abilitatea de a scrie în jurul aplicaţiilor cod C++ care îndeplineşte funcţia de
handler de excepţii.
Suportul sintactic C++ pentru tratarea excepţiilor se rezumă la trei cuvinte cheie,
a căror semantică preliminară o prezentăm în Tabelul xxxxx.
try{
//blocul try
//if(eroare) throw valoare_excepţie;
}
catch (Tip_excepţie Nume_variabilă ){
//Prelucrarea excepţiei
}
113
În cadrul acestei forme generalizate, valoarea valoare_excepţie lansată trebuie
să corespundă tipului Tip_excepţie .
#include <iostream.h>
void main()
{
cout<<"Start"<<endl;
try
{
cout<<"In interiorul blocului try…"<<endl;
throw 100;
cout<<"Nu se va executa…";
}
catch (int i)
{
cout<<"Am captat o excepţie --valoarea este:";
cout<<i <<endl;
}
cout<<"Sfarsit…";
};
#include <iostream>
void XHandler(int test)
{
cout<<"Inauntrul functiei XHandler, test are
valoarea:"<<test<<endl;
if(test) throw test;
114
};
void main()
{
cout<<"Start:"<<endl;
try
{
cout<<"Inauntrul blocului try…"<<endl;
XHandler(1);
XHandler(2);
XHandler(0);
}
catch(int i)
{
cout<<"Am captat o exceptie. Valoarea este:";
cout<<i<<endl;
};
cout<<"Sfarsit";
};
#include <iostream.h>
void XHandler(int test)
{
try
{
if(test) throw test;
}
catch(int i)
{
cout<<"Am captat exceptia nr.: "<<i<<endl;
}
};
void main()
{
cout<<"Start: "<<endl;
XHandler(1);
XHandler(2);
XHandler(0);
XHandler(3);
cout<< "Sfarsit";
115
};
try
{
//instrucţiuni
}
catch (tip1)
{
//tratare excepţie 1
}
catch(tip2)
{
//tratare excepţie 2
}
:
catch(tipn)
{
//tratare excepţie n
}
#include <iostream.h>
void XHandler(int test)
{
try
{
if(test==0)
throw test;
if(test==1)
throw "Sir de caractere…";
116
if(test==2)
throw 121.25;
}
catch(int i)
{
cout<<"Am captat exceptia #:"<<i<<endl;
}
catch(char *sir)
{
cout<<"Am captat exceptia de tip sir de
caractere:"<<sir<<endl;
}
catch(double d)
{
cout<<"Am captat exceptia #:"<<d<<endl;
}
};
void main()
{
XHandler(0);
XHandler(1);
XHandler(2);
cout<<"Sfarsit";
};
try
{
//Instructiuni
}
catch(…)
{
//tratarea exceptiei
}
117
Pentru exemplificare propun codul de mai jos.
#include <iostream.h>
void XHandler(int test)
{
try
{
if(test==0)
throw test;
if(test==1)
throw 'a';
if(test==2)
throw 121.25;
}
catch(…)
{
cout<<"Am captat o exceptie"<<endl;
}
};
void main()
{
cout<<"Start:"<<endl;
XHandler(0);
XHandler(1);
XHandler(2);
cout<<"Sfarsit";
};
Evident, prelucrările din cadrul blocului catch generic trebuie să fie independente
de tipul erorii.
Mecanismul captării excepţiilor explicite poate fi combinat cu mecanismul
excepţiilor generice ca în exemplul de mai jos.
#include <iostream.h>
void XHandler(int test)
{
try
{
if(test==0)
throw test;
if(test==1)
throw 'a';
if(test==2)
throw 121.25;
}
catch(int i)
118
{
cout<<"Am captat o exceptie de tip intreg…"<<endl;
}
catch(…)
{
cout<<"Am captat o exceptie generica"<<endl;
}
};
void main()
{
cout<<"Start:"<<endl;
XHandler(0);
XHandler(1);
XHandler(2);
cout<<"Sfarsit";
};
Restrictionarea exceptiilor
Pe măsură ce programele dumneavoastră devin mai complexe, ele vor apela
frecvent funcţii din cadrul unui bloc try. Atunci când programele dumneavoastră
apelează funcţii dintr-un bloc try, puteţi restricţiona tipurile de excepţii pe care
funcţia apelată le poate lansa. De asemenea puteţi preveni lansarea oricărei
excepţii dintr-o anumită funcţie.
119
#include <iostream.h>
void XHandler(int test) throw(int, char, double)
{
if(test==0)
throw test;
if(test==1)
throw 'a';
if(test==2)
throw 121.25;
}
void main()
{
cout<<"Start:"<<endl;
try
{
XHandler(0);
}
catch(int i)
{
cout<<"Am captat un intreg…"<<endl;
}
catch(char c)
{
cout<<"Am captat un caracter…"<<endl;
}
catch(double d)
{
cout<<"Am captat un double…"<<endl;
}
cout<<"Sfarsit";
};
#include <iostream.h>
void XHandler(void)
{
try
{
120
throw "Salve…";
}
catch(char *)
{
cout<<"Am captat char* in XHandler… "<<endl;
throw;
}
void main()
{
cout<<"Start…"<<endl;
try
{
XHandler();
}
catch(char *)
{
cout<<"Am captat char * in main…"<<endl;
}
cout<<"Sfarsit…";
};
#include <iostream.h>
void div (double a, double b)
{
try
{
if(!b) throw b;
cout<<"a/b="<<a/b<<endl;
}
catch(double b)
{
cout<<"Nu se poate imparti la zero…"<<endl;
}
}
void main()
{
double i,j;
do
121
{
cout<<Introduceti numaratorul (0 pentru stop):"<<endl;
cin i;
cout<<Introduceti numitorul :"<<endl;
cin j;
div(i,j);
} while (i!=0);
};
122
III Câteva aplicaţii care “au în spate”
C sau C++…
123
1 Sortarea elementelor unui şir de numere reale prin metoda bulelor.
Program pretext pentru modularizare. Se utilizează fişierul antet facilcrt.h
prezentat în continuarea acestei secţiuni.
#include <stdio.h>
#include <conio.h>
#include <stdlib.h>
#include"facilcrt.h"
#include<string.h>
void prelsir();
char prelopt();
void sort();
int dims;
float x[ _lms];
//Functia principala..
void main()
{
char op;
do
{
op=prelopt();
switch (op)
{
case '1': prelsir();
break;
case '2': sort();
break;
}
}
while (op!='3');
}
{
char c;
do
{
textcolor(RED);
124
textbackground(CYAN);
clrscr();
gotoxy(20,8) ;cprintf("Optiunile programului sunt:");
gotoxy(20,9) ;cprintf("1-Preluare elemente sir");
gotoxy(20,10);cprintf("2-Sortare/afisare sir");
gotoxy(20,11);cprintf("3-Terminare program");
makewin2(20,8,60,11);
makewin2(20,14,60,14);
gotoxy(20,14);cprintf("Optiunea Dvs.:");
c=getch();
}
while (strchr(_optiune,c)==NULL);
return c;
};
//Determinare maxim...
void sort()
{
float aux;
int i,sortat;
125
if (x[i]>x[i+1])
{
aux=x[i];
x[i]=x[i+1];
x[i+1]=aux;
sortat=0;
}
}
}
while(!sortat);
126
2 Fşier antet care încapsulează câteva facilităţi de lucru în mod text, utile
pentru realizarea unor interfeţe agreabile ale programelor cu utilizatorii.
Numit în exemplul precedent prin facilcrt.h.
#include <conio.h>
#include <stdio.h>
#include <string.h>
#define css 1
for (i=ls,j=cs;i<=j;i++,j--)
{
gotoxy(i,linie);
printf("%c",mesaj[i-ls]);
delay(50); //prototipul in fisierul antet <dos.h>
gotoxy(j,linie);
printf("%c",mesaj[strlen(mesaj)-1-cs+j]);
}
getch();
}
//Activare video-invers
void avideo()
{
textcolor(BLACK);
textbackground(WHITE);
}
//dezactivare video-invers
void dvideo()
{
textcolor(WHITE);
textbackground(BLACK);
}
127
sw=sw && ((ld-ls+1)>=strlen(sir));
if (sw)
{
col=ls+(ld-ls+1-strlen(sir))/2;
gotoxy(col,linia);
cprintf(sir);
}
}
128
printf("Coordonate ecran eronate!!!");
getch();
}
}
129
3 Aplicaţie pretext pentru lucru cu tablouri, folosind pointeri.
Mai precis, codul prezentat mai jos rezolvă următoarele probleme:
-alocarea dinamică a memoriei pentru un tablou bidimensional cu
număr de linii şi coloane precizate;
-citirea elementelor unui tablou bidimensional;
-transpunerea unei matrice;
-afişarea elementelor unei matrice;
#include <conio.h>
#include<iostream.h>
int i,j;
//Transpunerea matricei
void transpun(int **p,int **q,int nl,int nc)
130
{
for(i=0;i<nl;i++)
for(j=0;j<nc;j++)
q[j][i]=p[i][j];
};
//Functia principala
void main()
{
int **pmat;
int **q;
int nl=6,nc=2;
clrscr();
almemmat(pmat,nl,nc);
almemmat(q,nc,nl);
citmat(pmat,nl,nc);
transpun(pmat,q,nl,nc);
afismat(q,nc,nl);
getch();
};
131
4 Sortarea unui şir de numere întregi prin metoda HEAP_SORT. Se
utilizează reprezentarea liniară a unui arbore binar.
#include<iostream.h>
#include<conio.h>
132
while(y>0)
{
temp=list[0];
list[0]=list[y-1];
list[y-1]=temp;
--y;
walk_down(list,0,y);
}
}
void main()
{
list_type list;
int i,nrel;
clrscr();
gotoxy(20,10);
cout<<"Numar de elemente in lista:";
cin>>nrel;
clrscr();
for(i=0;i<nrel;i++)
{
gotoxy(20,11);
clreol();
cout<<"Elementul[ "<<i<<" ]=";
cin>>list[i];
}
heap_sort(list,nrel);
clrscr();
for(i=0;i<nrel;i++)
cout<<list[i]<<endl;
getch();
}
133
5 Rasfoirea unui fişier text şi contorizarea apariţiilor unui caracter într-un
fisier text utilizând fluxuri binare.
#include <fstream.h>
#include <stdlib.h>
#include "facilcrt.h"
char optiuni[]="1234";
char *nf;
int sfs;
134
int cont;
clrscr();
gotoxy(20,10);
cout<<"Caracterul:";
makewin2(20,10,60,11);
gotoxy(20+strlen("Caracterul:"),10);
cin>>c;
if(sfs)
{
ifstream in(nf,ios::in | ios::binary);
if(!in)
{
sfs=0;
gotoxy(1,24);
textcolor(RED+BLINK);
cprintf("Fisierul indicat nu poate fi deschis...");
getch();
sfs=0;
textcolor(WHITE);
}
else
{
char ch;
cont=0;
while(in)
{
in.get(ch);
if(ch==c)
cont++;
}
gotoxy(20,11);
cout<<"Numar de aparitii:"<<cont;
getch();
}
}
else
{
gotoxy(1,24);
textcolor(RED+BLINK);
cprintf("Selectati mai intai fisierul...");
getch();
textcolor(WHITE);
}
}
135
{
char *n;
sfs=1;
clrscr();
gotoxy(20,10);
cout<<"Numele fisierului de rasfoit:";
makewin2(15,10,65,10);
do
{
strcpy(n,"");
gotoxy(20+strlen("Numele fisierului de rasfoit:"),10);
gets(n);
}
while(strlen(n)==0);
return n;
}
136
clrscr();
}
cout<<ch;
}
getch();
}
}
else
{
gotoxy(1,24);
textcolor(RED+BLINK);
cprintf("Selectati mai intai fisierul...");
getch();
textcolor(WHITE);
}
}
137
6 Modelarea C a unor facilităţi de lucru cu memoria video în mod text.
Exemplul de cod prezentat mai jos ilustrează, în alt context, utilitatea
pointerilor, precum şi o serie de elemente tehnice referitoare la
scrierea/citirea în/din memoria video, atunci când se lucrează în mod text.
#ifndef _stdio_h
#include<stdio.h>
#define _stdio_h
#endif
#ifndef _conio_h
#include<conio.h>
#define _conio_h
#endif
#ifndef _string_h
#include<string.h>
#define _string_h
#endif
#define startcol1 16
#define startcol2 36
#define startcol3 56
#include<dos.h>
#include<iostream.h>
#include<stdlib.h>
138
//Functia seteaza adresa memoriei video in functie de
//modul video curent
void setvptr()
{
int vmod;
vmod=mod_video();
if((vmod!=2)&&(vmod!=3)&&(vmod!=7))
{
cout<<"Video trebuie sa fie in modul text cu 80 de
coloane.";
exit(1);
}
if(vmod==7)
mem_vid=(char far *) 0xB0000000;
else mem_vid=(char far *)0xB8000000;
}
139
gotoxy(1,1);
for(int i=0;i<78;i++)
printf("\xf");
gotoxy(16,2);printf("Cod Caracter");
gotoxy(36,2);printf("Cod Caracter");
gotoxy(56,2);printf("Cod Caracter\n");
for(i=0;i<78;i++)
printf("\xf");
}
140
case 2:
{
cc=startcol2;
break;
}
case 3:
{
cc=startcol3;
break;
};
}
lin++;
gotoxy(cc,nrcan+lin);printf("%i",i);
gotoxy(cc+7,nrcan+lin);printf("%c",(char)i);
};
gotoxy(1,23);
for(j=0;j<78;j++)
printf("\xf");
gotoxy(1,24);
printf("Pentru terminare apasati o tasta....");
getch();
}
141
7 Modelarea C++ a operaţiilor cu şiruri de caractere. Problemă pretext
pentru aplicarea corectă a principiului încapsulării, pentru utilizarea în
funcţie de cerinţe a constructorilor în specificarea comportamentului unei
clase, pentru redefinirea unor operatori uzuali în lucru cu şiruri de
caractere (concatenarea polimorfică, adresarea unui caracter al tabloului
de caractere, etc.).
#include <stdio.h>
#include<string.h>
//*****************************************************
//Clasa modeleaza lucrul cu siruri de caractere in C++
//class string
{
int lsir; //lungimea sirului
char *psir; //tablou de caractere alocat dinamic
public:
142
string operator+(string & x);
//Destructor clasa
~string();
//*****************************************************
//Implementare functii membre
143
//Implementare redefinire operator atribuire obiect
void string::operator=(string x)
{
delete psir;
lsir=x.lsir;
psir=new char[lsir];
strcpy(psir,x.psir);
}
144
//Implementare concatenare a doua siruri
string string::operator+(string & x)
{
string s;
strcat(s.psir,psir); strcat(s.psir,x.psir);
s.lsir=lsir+x.lsir-1;
return s;
}
//Implementare destructor
string::~string()
{
delete psir;
}
145
};
146
8 Aplicatie care ilustreaza lucrul cu fisiere cu tip in C++
Din nou o problemă pretext, care se referă la actualizarea unui fişier în care
se păstrează întrebările unui test şi răspunsurile corecte, presupunând că o
întrebare are mai multe alternative de răspuns.
#include <conio.h>
#include <stdio.h>
#include <iostream.h>
#include <ctype.h>
#include<stdlib.h>
#include <string.h>
#include <fstream.h>
char optiuni[]="1234567";
char *nf;
int sfs,nri;
//Tipul inregistrare
typedef struct struct_test
{
int nr_intrebare;
int alt_corecta;
} t_inr_fis;
//Variabila inregistrare
t_inr_fis inr;
147
void main()
{
char op;
sfs=0;
do
{
op=prelopt();
switch(op)
{
case '1': nf=selnumef();
break;
case '2': crefis();
break;
case '3': adinfis();
break;
case '4': {
clrscr();
gotoxy(10,12);
cout<<"Numar intrebare inregistrare de
sters:";
cin>>nri;
steinfis(nri);
break;
};
case '5': vizfis();
break;
case '6': {
clrscr();
gotoxy(10,12);
cout<<"Numar inregistrare de selectat:";
cin>>nri;
selin(nri);
break;
};
case '7': break;
}
}
while (op!='7');
};
148
ftestin.read((unsigned char*) &inr,sizeof(t_inr_fis));
if(!ftestin.eof())
{
clrscr();
gotoxy(10,12);
cout<<"Nr. intrebare :";
cout<<inr.nr_intrebare;
gotoxy(10,13);
cout<<"Alternativa corecta:";
cout<<inr.alt_corecta;
getch();
ftestin.close();
};
}
else
{
gotoxy(1,24);
cout<<"Selectati sau creati mai intai un fisier!....";
getch();
}
};
149
//Implementare crefis()
void crefis()
{
ofstream ftestout;
clrscr();
gotoxy(20,10);
cout<<"Nume fisier de creat:";
cin>>nf;
ftestout.open(nf,ios::out|ios::binary);
sfs=1;
char ras;
do
{
clrscr();
gotoxy(20,10);cout<<"Numar intrebare:";
cin>>inr.nr_intrebare;
gotoxy(20,11);cout<<"Raspuns corect :";
cin>>inr.alt_corecta;
ftestout.write((unsigned char *) &inr,
sizeof(t_inr_fis));
gotoxy(20,12);cout<<"Mai aveti de introdus(D,N):";
ras=getch();
}
while (!(toupper(ras)=='N'));
ftestout.close();
}
//Implementare adinfis()
void adinfis()
{
if (sfs)
{
ofstream ftestout;
clrscr();
gotoxy(20,10);
ftestout.open(nf,ios::app|ios::binary);
char ras;
do
{
clrscr();
gotoxy(20,10);cout<<"Numar intrebare:";
cin>>inr.nr_intrebare;
gotoxy(20,11);cout<<"Raspuns corect :";
cin>>inr.alt_corecta;
ftestout.write((unsigned char *) &inr,
sizeof(t_inr_fis));
150
gotoxy(20,12);cout<<"Mai aveti de introdus(D,N):";
ras=getch();
}
while (!(toupper(ras)=='N'));
ftestout.close();
}
else
{
gotoxy(1,24);
cout<<"Selectati sau creati mai intai un fisier!....";
getch();
}
}
//Implementare vizfis()
void vizfis()
{
if (sfs)
{
ifstream ftestin;
ftestin.open(nf,ios::in|ios::binary);
while (!ftestin.eof())
{
clrscr();
ftestin.read((unsigned char*)&inr,sizeof(t_inr_fis));
if(!ftestin.eof())
{
gotoxy(10,12);
cout<<"Nr. intrebare :";
cout<<inr.nr_intrebare;
gotoxy(10,13);
cout<<"Alternativa corecta:";
cout<<inr.alt_corecta;
getch();
};
};
ftestin.close();
}
else
{
gotoxy(1,24);
cout<<"Selectati sau creati mai intai un fisier!....";
getch();
}
}
151
//Implementare selnumef()
char *selnumef()
{
char *n;
sfs=1;
clrscr();
gotoxy(20,10);
cout<<"Numele fisierului de lucru:";
do
{
strcpy(n,"");
gotoxy(20+strlen("Numele fisierului de lucru:"),10);
gets(n);
}
while(strlen(n)==0);
return n;
}
//Implementare prelopt()
char prelopt()
{
clrscr();
gotoxy(20,8);
cout<<"Optiunile programului...";
gotoxy(20,9);
cout<<"__________________________________________";
gotoxy(20,11);
cout<<"1-Selectare fisier structura test";
gotoxy(20,12);
cout<<"2-Creare fisier structura test";
gotoxy(20,13);
cout<<"3-Adaugare date in fisierul structura test";
gotoxy(20,14);
cout<<"4-Stergerea unei inregistrari din fisier";
gotoxy(20,15);
cout<<"5-Vizualizare date fisier structura";
gotoxy(20,16);
cout<<"6-Afisare inregistrare de numar specificat";
gotoxy(20,17);
cout<<"7-Terminare program";
gotoxy(20,19);
cout<<"__________________________________________";
gotoxy(20,20);
cout<<" Alegerea Dvs.:";
gotoxy(20+strlen(" Alegerea Dvs.:"),20);
char op;
152
do
op=getch();
while (strchr(optiuni,op)==NULL);
return op;
};
153
BIBLIOGRAFIE MINIMALĂ
[1]. N. Barkakati Borland C++ 4, Ghidul programatorului,
Editura Teora, 1997
[2]. K., Jamsa, L. Klander Totul despre C şi C++, Editura Teora,
2000
[3]. I. Muşlea C++ pentru avansaţi, Editura
Microinformatica, 1994
[4]. L. Negrescu Limbajele C şi C++ pentru începători,
Limbajul C++, volumul 2, Editura Albastră,
Cluj-Napoca, 2000
[5]. L. Negrescu Limbajele C şi C++ pentru începători,
Limbajul C, volumul 1, Editura Albastră,
Cluj-Napoca, 2000
[6]. D. M. Popovici, C++. Tehnologia orientată pe obiecte.
I. M. Popovici, Aplicaţii,
I. Tănase Teora, 1996
Lista cărţilor din care se poate învăţa câte ceva despre C++ si lumea
POO este deschisă, la fel de deschisă ca mintea oricărui student care nu
“şi-a greşit culoarul” pe care aleargă pentru a deveni un profesionist.
154
Subiecte pentru examenul la disciplina
Programare Obiect Orientată, Anul II INFO
FUNDAMENTE
ÖOperatori în C/C++.
ÖTablouri C/C++.
155
ÖUniuni.
PARADIGMA POO
ÖFluxuri C/C++.
156
Scurte consideraţii relativ la “problema evaluării” la
disciplina POO
157
face pentru a confirma superioritatea omului în raport cu alte
vieţuitoare şi nu numai.
Pentru a mă face mai bine înţeles, în cele ce urmează voi “arunca”
câteva idei care ar vrea să sugereze modul în care, ca profesor,
înţeleg să evaluez lucrarea unui student.
158