Documente Academic
Documente Profesional
Documente Cultură
Titlul subproiectului:
pentru disciplina
FUNDAMENTELE PROGRAMĂRII
Lect. univ. dr. Iulian FURDU
2019
Câteva cuvinte despre disciplina
FUNDAMENTELE PROGRAMĂRII
Obiectivul principal al acestui curs este de a forma acele idei de bază și abilități
de care studenții (și nu numai) au nevoie în activitatea de programare, pe orice
platformă, indiferent de limbaj. Altfel spus obiectivul disciplinei în termeni de
competenţe profesionale se referă la programarea în limbaje de nivel înalt, în sens
general, în timp ce competenţele specifice (competenţele asigurate de programul de
studiu) vizează: 1. Descrierea adecvată a paradigmelor de programare şi a
mecanismelor de limbaj specifice, precum şi identificarea diferenţei dintre aspectele
de ordin semantic şi sintactic. 2. Explicarea unor aplicaţii soft existente, pe niveluri de
abstractizare (arhitectură, pachete, clase, metode) utilizând în mod adecvat
cunoştinţele de bază 3. Elaborarea codurilor sursă adecvate şi testarea unitară a unor
componente într-un limbaj de programare cunoscut, pe baza unor specificaţii de
proiectare date. 4. Dezvoltarea de unităţi de program şi elaborarea documentaţiilor
aferente.
2
CUPRINS
I. Algoritmi 5
V. Tipuri structurate.Tablouri 30
VI. Structuri 33
VIII. Subprograme 38
IX. Recursivitate 42
X. Șiruri de caractere 46
XI. Fișiere 53
Bibliografie 72
3
4
I. Algoritmi
Din acest punct de vedere este evident că pentru rezolvarea unei probleme pot
exista mai mulţi algoritmi.
Paşi/Instrucţiuni
Date de Date de
intrare ieşire
- date de intrare
- paşii algoritmului sau instrucţiunile
- date de ieşire.
De asemeni, distingem trei tipuri de paşi folosiţi în descrierea unui algoritm (vezi
structuri).
5
Caracteristicile algoritmilor
Sensul unui algoritm este undeva similar cu cel de reţetă, proces, metodă,
tehnică, procedură sau rutină cu deosebirea că el reprezintă un set finit de reguli care
indică o secvenţă de operaţii în vederea rezolvării unei probleme şi are următoarele
caracteristici importante: finitudinea, generalitatea, corectitudinea, unicitatea (v.
resursa [1]).
Descrierea algoritmilor în cadrul programării procedurale se poate realiza prin
diverse modalităţi:
a) schemele logice presupun reprezentarea în mod grafic a paşilor algoritmului,
prin figuri geometrice care să permită o viziune clară, de ansamblu a algoritmului.
Acest mod de descriere este eficient pentru programe nu foarte mari, întrucât o astfel
de schemă devine dificil de citit pe măsură ce numărul de paşi creşte.
b) pseudocod este un limbaj apropiat de limbajul natural, descris de regulă în
limba cea mai apropiată utilizatorului. Descrierea în pseudocod permite o retranscriere
uşoară în diverse limbaje de programare, o exprimare independentă de particularităţile
de limbaj de programare. Pseudocodul este cel mai frecvent utilizat în descrierile
generale ale metodelor şi algoritmilor clasici.
c) limbajele de programare descriu algoritmii folosind un limbaj specific, artificial,
cu o sintaxă şi semantică bine definite
Mai multe: v.resursa [2]. Tot aici găsiți un intro bun pentru mediul de programare
CodeBlocks, folosit la laborator (alternative free: Visual Studio varianta academic,
DevCpp etc.). Urmând instrucțiunile de la resursa [2], creați și compilați programul
Hello world.
Compilator. Interpretor.
6
Fişier sursă
se includ
Preprocesor
fişierele
header etc.
Compilator
link-editor (leagă
toate modulele
obiect)
Comanda
”Run”
Fişier
executabil
date intrare date ieşire
Figura 2. Paşii pentru construirea unui program C (schema simplificată)
{ secvenţă de instrucţiuni
="corpul" programului
(zona operativă -
obligatorie)
} // end main
} secvenţă de instrucţiuni
Notă: într-un program funcţiile 1...N pot fi scrise şi înainte de main sau pot fi
date doar semnăturile lor, urmând ca focusarea pe detaliile de implementare (codurile
subprogramelor) să aibă loc după main.
De exemplu, în programul:
#include <stdio.h>
void main(void)
{ printf("Program in C");
}
<tip1> numevar1 (sau listă variabile, unde listă variabile reprezintă o mulţime
de variabile de acelaşi tip, separate prin virgulă);
<tip2> numevar2;
Obs. Algoritmul unui program este alcătuit din minim o etapă. În cadrul fiecărei
etape se realizează minim o singură acţiune. Fiecare acţiune (comandă) se realizează
printr-o linie program. Totalitatea acţiunilor unui program, respectiv totalitatea liniilor
program, alcătuiesc corpul programului.
2. limbaj pseudocod:
1. START
2. CITESTE A,B
3. DACĂ A≠0 ATUNCI
4. CALCULEAZĂ (ATRIBUIE) x=-B/A
5. SCRIE (AFIȘEAZĂ) x
ALTFEL (subînțelegem A=0)
6. DACĂ B=0 ATUNCI
7. SCRIE “EC. NEDETERM sau O INFINITATE DE SOLUTII”
ALTFEL
8. SCRIE “EC. IMPOSIBILA sau EC NU ARE SOLUTII”
9. STOP
Remarcăm lipsa echivalența aproape unu la unu între schema logică si limbajul
pseudocod. Este ALTFEL instrucțiune separată sau face parte din sintaxa DACA (IF)?
Dar ATUNCI?
3. Limbaj de programare (cod C, CodeBlocks, v. resursa [2]) de compilat și rulat
individual:
#include <iostream>
int main()
{
int a,b;
float x;
cin>>a>>b;
if(a==0)
if(b==0)
cout<<"Ecuatia are o infinitate de solutii.";
else
cout<<"Ecuatia nu are solutii!";
else
{
x=-b/a;
cout<<"Solutia ecuatiei este"<<x;
}
return 0;
10
}
Remarcăm lipsa echivalenței formale unu la unu între limbaj pseudocod și cod C.
Puteți genera cod pentru problema rezolvării ecuației de gradul II?
Soluția:
#include <iostream>
#include<cmath>
using namespace std;
int main()
{ float a,b,c,x,x1,x2,D;
cin>>a>>b>>c;
if(a==0)
if(b==0)
if(c==0)
cout<<"Ecuatia are o infinitate de solutii.";
else
cout<<"Ecuatia nu are solutii.";
else
cout<<"Solutia ecuatiei este:"<<-c/b;
else
{ D=pow(b,2)-4*a*c;
if(D<0)
cout<<"Ecuatia nu are solutii reale.";
else if(D==0)
{ cout<<"Ecuatia are solutii egale: ";
cout<<"x1=x2="<<-b/2*a;
}
else
{ x1=(-b-sqrt(D))/2*a;
x2=(-b+sqrt(D))/2*a;
cout<<"Prima solutie: "<<x1<<endl;
cout<<"A doua solutie: "<<x2<<endl;
}
}
}
11
Literele mari au în ordine codul ASCII de la 65 la 90, literele mici de la 97 la
122, iar cifrele de la 48 la 57.
Obs. Literele (mari, mici) ce fac parte din alfabetul limbii engleze (26 caractere)
au codurile corespondente de mai sus, nu şi diacriticele din limba română. Acestea
aparţin codului ASCII extins ISO 8859-16 ce conţine un total de 256 caractere. Lista
codurilor ASCII se poate găsi la (v.resursa [4])
Specificaţii de limbaj C
Vocabular:
• setul de caractere: a - z , A - Z , 0 - 9;
• semne de punctuaţie: ‘ . ‘ , ‘ , ‘ , ‘ ; ‘ etc.;
• caractere speciale : % , & , $ etc.;
• cuvinte rezervate : while, if, for, else, void, char, int, float, double, unsigned,
include, main, getch etc.
Obs. Există 32 cuvinte rezervate în C. Acestea nu pot fi folosite decât în contextul
pentru care au fost create.
Constante şi expresii
Obs. Înţelegem prin identificator un nume care defineşte de regulă în mod unic
o entitate a programului: constantă, variabilă, funcţie etc.
Separatori
/*
<comentariu>
Operatori. Operaţii.
Un operator (aritmetic, logic, special etc.) este simbolul sau simbolurile ce descriu
aplicarea unei operaţii, cea corespunzătoare operatorului. Funcţie de context anumiţi
operatori pot avea semnificaţii diferite- respectiv acelaşi simbol poate defini operaţii
diferite. Tabelul cu operatori şi priorităţile lor (v.resursa [5]). Operatorul cel mai uzual
este cel de atribuire.
Precizări.
<expresie1><operator>= <expresie2>
3. Operatorul modulo (%) operează doar asupra întregilor. Împărţirea (/) operează
şi asupra numerelor reale. Rezultatul împărţirii a= 1/2 este 0 chiar dacă „a” a fost
declarat float!. Dacă ambii termeni ai unei împărţiri sunt întregi atunci se face
împărţirea întreagă. Pentru un rezultat corect va trebui ca măcar unul din termeni să
fie scris sub forma unui real, cel mai corect fiind a= 1.0 / 2.0 sau folosirea casting-ului
(vezi conversii de tip).
4. Post şi preincrementare (sau forma prefixată şi postfixată; similar pentru
decrementare)
codul x= i++; este echivalent cu x= i; i= i+1;
y= ((++a)-(b--))*25; avem:
a= a+1;
y= (a – b)* 25;
b= b-1;
13
III. Date şi modalităţi de reprezentare a datelor
Tipuri de date
Un tip de date constă într-o mulţime de valori pentru care există un mod de
reprezentare în memoria calculatorului, cât şi o mulţime de operatori, care pot fi aplicaţi
acestor valori.
Obs. Declararea unui anumit tip de date determină alocarea de către compilator a unei
zone de memorie de lungime specifică tipului de date declarat.
Tipuri de date uzuale: char, int, float, double, void (tipuri fundamentale în C), tipul
tablou, tipul înregistrare, tipul fişier (tipuri structurate).
Tipuri de bază predefinite (standard) : tipurile întregi (int), tipurile reale (float,
double), tipul caracter (char), tipul logic (bool C++) tipul şir de caractere (string în
C++);
Alte tipuri: tipul fişier ( FILE ), tipul adresă, tipul referinţă (pointer) etc.
Obs. Tipurile de date pot avea calificatori gen: short, long, etc. Specific tipurilor întregi
sunt calificatorii signed şi unsigned care prefixează tipul. Folosind signed se pot
reprezenta valori pozitive şi negative, în timp de folosind unsigned se pot reprezenta
doar valori pozitive inclusiv zero).
Spaţiu
Denumire Descriere ocupat Domeniu de valori*
(bytes)*
signed: -128 .. 127
char Character 1
unsigned: 0 .. 255
signed: -2147483648 ..
2147483647
int Integer 4
unsigned: 0 to
4294967295
long int
Long integer 4 signed: -2147483648 ..
(long)
2147483647
14
unsigned: 0 ..
4294967295
Simplă precizie.
float Număr în virgulă 4 +/- 3.4e +/- 38 (~7 digits)
flotantă
Dublă precizie.
+/- 1.7e +/- 308 (~15
double Număr în virgulă 8
digits)
flotantă
Observaţii:
revenire la capăt de
‘ \r ‘ CR (carriage return)
rând
15
‘\”‘ double quote () afişează un “
Constante
Constantele reale sunt numere reale virgula zecimală fiind înlocuită prin punct. Se pot
defini în două forme :
a) forma fără exponent ce trebuie să conţină minim o cifră atât la partea întreagă
cât şi la cea zecimală 0.0 -12.39 1.0
b) forma cu exponent, când pot lipsi atât punctul cât şi partea zecimală, litera E
(sau D) însemnând "10 la puterea": 34E12 (34E+12 ) sau 34D12 (adică 34*1012), -
45.2E-3 (respectiv –45.2*10 –3 )
o constantă reală poate fi scrisă în mai multe feluri : 18200 = 18.2E3 = 1.82E4 =
0.182E5 = 1820E1
Funcţie de numărul de octeţi ocupat, constantele reale sunt de două subtipuri :
• float: cuprinse între 1.2 E-38 şi 3.4 E38 ;
• double: cuprinse între 1E-296 şi 3.4E38 ;
Constantele reale pot fi declarate cu sufixul “f”/”F” sau “l”/”L”:
60.328f // float
6.05e-4L // long double
16
Exemple : ' Bit ', " Oberon ". Simbolurile de încadrare nu pot fi alternate pentru
aceeaşi constantă fie ea caracter sau şir de caractere fiind incorect : " program ' sau
invers ' program ". Un şir de caractere nu poate conţine în interior un caracter apostrof
sau ghilimele.
Constante logice.
Există trei constante logice:
bool foo = true;
bool bar = false;
int* p = nullptr; // pointer null
Constante simbolice. O constantă simbolică este desemnată printr-un identificator
ce se poate ataşa unei valori constante de orice tip şi nu poate fi modificat în timpul
execuţiei programului. Constatele de acest tip se numesc şi literali.
Exemple : m=12, max=5 etc.
-Dacă declarăm mai multe constante cuvântul const se scrie de fiecare dată;
-Se pot declara mai multe constante avănd aceeaşi valoare precum const a,b 3;
-Tipul unei constante (real, caracter, etc.) rezultă din chiar valoarea sa.
Obs. Constantele simbolice (INT_MAX, FLT_MIN etc.) sunt definite conform
standardului C++ în biblioteca <climits.h> însă pot depinde de compilator şi mediu de
programare. Pentru compilatoarele din KDevelop şi Microsoft C++ acestea se află în
limits.h în timp ce pentru distribuţiile Borland C++ Builder sunt în bibilioteca _lim.h. În
DevCpp acestea sunt în limits.h cît şi în values.h.
Variabile
O variabilă, spre deosebire de o constantă, îşi poate modifica valoarea în cadrul unui
program. Ea este caracterizată de:
1. Identificator (nume);
2. Domeniul de vizibilitate (zona de program în care variabila are influenţă/efect )
<engleză: scope >;
OBS. Din acest punct de vedere pot exista într-un program 2 variabile cu acelaşi
identificator ( int a , A = 2 ).
17
3. Valoare;
4. Tipul de date (indică spaţial de memorie alocat/ocupat);
5. Adresă.
O variabilă poate primi drept valoare:
─ o valoare constantă;
Operatori. Expresii.
Operatorul de atribuire
nume_funcţie = expresie ;
Efect:
1) se evaluează expresia;
2) variabila (funcţia) primeşte valoarea expresiei; tipul expresiei se va converti
înainte de atribuire la tipul variabilei (funcţiei), dacă nu coincide cu acesta. Spre
exemplu, dacă expresia are valoare întreagă şi variabila ce o referă este declarată
reală, se va converti valoarea expresiei în tipul real.
pentru ca atribuirea să aibă loc trebuie ca tipul variabilei (funcţiei) să fie
compatibil cu valoarea expresiei. Altfel, se semnalează eroarea de sintaxă
"incompatible assignment".
18
sunt corecte atribuirile de forma x:= x+p, ceea ce s-ar traduce prin : noua valoare
a lui x este egală cu cea veche (anterioară) plus "p".Această nouă valoare este
memorată ca fiind valoarea actuală a lui x, cea veche pierzându-se.
Valoarea atribuită unei variabile poate fi:
Exemplu comentat:
#include <conio.h>;
#include <math.h>;
const d4; const pi 3.141;
int a,i,j; longint x,y;
char c;
float alfa,aria,s,u ,c1,c2;
bool er;
void main()
{
a=MIN_INT; /* lui a i se atribuie val constantei predefinite
MININTEGER*/
x=3; //x primeşte val constantă 3
c="t";
y=d; //y primeşte val constantei simbolice d=4
x=y; // x primeşte val variabilei y(d) deci x va fi 4
x=x-1; // x va avea val 3
c=4; c2=5;
alfa=pi/6; //var alfa i se atribuie val expresiei pi/6
u=f.Sin(alfa); // var u ia valoarea returnată de fcţ
sin apelată cu parametrul pi/6, fct predefinită sin se afla în
biblioteca math.h
aria=c1*c2*u/2 ; // aria ia val expresiei c1*c2*u/2
er=x>y; // 3>4 fals, err va avea val FALSE
er=c2 IN n; // TRUE întrucât c2 aparţine domeniului lui n
a=c1/c2; // incorect întrucât "a" a fost declarată de tip
integer iar expresia este de tip real:4/5*)
Conversii de tip
Când variabilele din expresie sunt de tipuri diferite, are loc o conversie de tip, după
regula: valoarea expresiei este convertită la valoarea variabilei din membrul stâng. De
exemplu, în cazul secţiunii
int k;
char ch;
float f;
void func(void)
{
ch=k; /* caz 1*/
k=f; /* caz 2*/
19
f=ch; /* caz 3*/
f=k; /* caz 4*/
}
au loc următoarele conversii de tip:
▪ În cazul 1, cei opt biţi din stânga (de ordin mai mare) sunt eliminaţi şi se atribuie lui
ch cei opt biţi de ordin mai mic. Dacă x este cuprins între 0 şi 255, ch şi k vor avea
valori identice.
▪ În cazul 2, k va primi partea întreagă a lui f.
▪ În cazul 3, valoarea conţinută de cei opt biţi ai lui ch este convertită în virgulă
mobilă.
▪ În cazul 4, valoarea întreagă a lui k este convertită în virgulă mobilă.
Când se converteşte un întreg într-un caracter, se pierd primii 8 biţi, iar când se
converteşte un întreg lung într-un întreg, se pierd primii 16 biţi.
Atunci când într-o expresie sunt amestecate constante şi variabile de diferite tipuri, ele
sunt convertite la tipul cel mai mare de către compilator.
intrare
a1,
a2,
20
ieşire
Exemplu: se citesc trei două variabile întregi x,y; să se calculeze şi să se afişeze media
lor aritmetică
a1: citeşte x;
a2: citeştey;
a3: atribuie lui m:= (x+y)/2;
a4: afişează m;
Exemplu: să se scrie un program care să schimbe între ele două valori întregi;
aux := x ; // se foloseşte var aux ca variabilă de manevră
x := y ;// acest mod de intershimb se mai numeşte metoda
paharelor
y := aux;
Obs. din punct de vedere al programării structurate secvenţa este tot o structură. Mai
multe instrucţiuni scrise una după alta formează o secvenţă. În Pascal secvenţa este
delimitată de BEGIN şi END, în C, C++, Java, Pearl etc. este delimitată de acolade,
care ţin loc de BEGIN şi END. În Oberon, deoarece fiecare structură, cu excepţia
secvenţei are un END final, secvenţele cuprinse în acea structură sunt precis
delimitate neexistănd necesitatea unui BEGIN, acesta fiind suplinit de însuşi numele
structurii.
în funcţie de
caz
intrare ieşire
cazul 1: a1,
cazul 2: a2,
cond
B A
21
Sintaxă: if <conditie> then A
[else] B
Obs. În cazul în care A sau B lipsesc obţinem o structură decizională cu ramură vidă.
Semantica: se evaluează condiţia. Dacă aceasta este adevărată se execută <A>, iar
dacă nu, se execută <B>;
selector
secv 1 secv n+1
secv 2 secv n
Exemplu: int i;
cin>>i ;
swich(i)
{case 1: {cout<<”Introduceti date“ >>; break;}
case 2: {cout<<”Afisare date”; break;}
default: cin<<”Iesire”;
}
Obs. Prezenţa instrucţiunii break la evaluarea unui anumit caz determină părăsirea
switch, pentru a nu se evalua inutil restul cazurilor. Selectorul trebuie să aparţină unui
tip ordinal.
CONCLUZII :
În cazul structurii decizionale, spre deosebire de structura secvenţială, ordinea
fizică (a scrierii) a prelucrărilor nu mai coincide cu parcursul lor logic;
Structurile liniară şi decizională se pot compune atât în secvenţă (una după
cealaltă), numită compunere prin coordonare, cât şi ierarhic, prin subordonare,
structura decizională conţinând structuri liniare pentru descrierea acţiunilor;
22
Structurile decizionale se pot compune între ele prin subordonare, fiecare bloc
interior legându-se în structură printr-o singură intrare îi o singură ieşire;
INSTRUCŢIUNI REPETITIVE
Clasificare:
a. cu contor în creştere ;
b. cu contor în descreştere.
Condiţia de
repetiţie
Secvenţa
de
acţiuni
Intrucţiune: while…do
Sintaxa: while(<cond>)
[do]
set instruct <A>
Semantica: se evaluează expresia <cond>: dacă valoarea obţinută în urma evaluării
expresiei este nenulă se execută setul de instrucţiuni A şi se revine la pasul 1, în caz
contrar se trece la instrucţiunea imediat următoare.
int i=1;
int n=10;
while(i<=n)
23
{cout<<i<<” “ ;
i++;
}
Obs. Setul de instrucţiuni A trebuie să conţină minim o instrucţiune care să afecteze
valoarea testului astfel încât acesta să devină la un moment dat fals (fapt ce determină
părăsirea buclei, altfel are loc o ciclare infinită şi sistemul se blochează).
s := 0 ; i := 2;
WHILE i <= n DO
s :=s+i ;
i :=i+2 ;
END;
24
A2. Repetiţia cu test final
Secvenţa
de
acţiuni
Condiţia de
ieşire
repetiţie
Exemplu: do
{cout<<i<<” “;
i++;
}
while(i <= n)
Instrucţiunea: do…while
Sintaxă: do
<B>;
while( <cond> )
instructiunea_urm;
Semantică:
─ se execută B până când <cond> devine falsă (cât timp cond rămâne adevarată).
Obs:
Exemplu: S = 1 + 2 + … + n
int i=1; s=0;
int n=10;
25
while (i <= n)
{
S = S + i; i++; // S+=i++;
cout<<”Suma la pasul ”<<i<<”: ”<<S<<endl;
}
contor := vi
v < = vf
Secventa
de actiuni
de repetat
v := v + pas
Instrucţiunea FOR
Semantică:
Obs. Dacă vi<vf atunci rezultă for cu contor în creştere, în caz contrar for cu contor
în descreştere.
Principiul de execuţie:
- se evaluează expr1;
-împărţim a la b ( b≠0);
c.m.m.d.c.(a,b)=c.m.m.d.c.(b,a mod b)
#include <iostream>
using namespace std;
int a, b, deimp, imp, cmmdc, rest;
int main()//void pentru medii
do
{
cout<<”Dati a şi b”<<endl;
cout<<”a= “;cin>>a;
cout<<endl;
cout<<”b= “;cin>>b;
cout<<endl;
deimp=a;imp=b;
do
{rest=deimp%imp;
deimp=imp;
imp=rest;
}
while(imp!=0)
cmmdc=deimp;
cout<<”continuati (d/n)”<<endl;
cin>>ok;
while(ok=”n”)
cout<<”c.m.m.d.c.”<<cmmdc;
system(“Pause”);
getchar();
return 0;
}
Determinarea cmmdc prin scăderi repetate:
# include <iostream.h>
27
# include<conio.h>
int main(){
int a,b,u,v,cmmdc,cmmmc;
cout<<”dati a: “; cin>>a;
cout<<endl;
cout<<”dati b: “; cin>>b;
cout<<endl;
if(a*b==0 ) \\ a sau b sunt 0
{if(a + b != 0)
{cout<<”cmmdc este: “<<a+b;
cout<<endl; }
else
cout<<”cmmmc=0“;
}
else {
u=a;
v=b;
while(a*b)
if(u > v)
u=u-v;
else
v=v–u;
cmmdc=u;
cmmmc=(u*v)/cmmdc;
cout <<”cmmdc= “<<cmmdc<<”cmmmc= “<<cmmmc;
}
system(“pause”);
getch();
}
• Dintre toate structurile prezentate trei sunt cosiderate de bază: structura
secvenţială, structura decizională binară şi structura repetitivă condiţionată anterior,
restul structurilor fiind de fapt structuri auxiliare, acceptate pentru larga lor utilizare,
ce pot fi însă reproduse prin intermediul structurilor de bază.
Aplicații:
28
2. Calculul unor sume S=1-1/2+1/3-1/4….(for combinat cu if)
1. #include <math.h>
#include <iostream>
#include <math.h>
using namespace std;
int i,n;float p;
int main()
{ cin>>n;
for(i=1;i<=n;i++)
{ if(i%2==0)
p=p-1.0/i;
else
p=p+1.0/i;
}
cout<<p;
return 0;
}
3. Calculul unor funcții acoladă (if-uri imbricate, determinați singuri forma funcției
după codul de mai jos, compuneți un exemplu nou, domeniul funcției interval închis)
#include <iostream>
#include <math.h>
using namespace std;
float x;float e=2.71;
int main()
{
cin>>x;
if(x>0)
cout<<pow(x,2)-sqrt(x);
else
if(x==0)
cout<<-5;
else
cout<<arcsin(x)+pow(e,x);
return 0;
}
4. Calcul sume, produse, aici suma de la 3 la n din x2/ex (s.repetitive, imaginați alte 2
exemple)
#include <iostream>
#include <math.h>
using namespace std;
int i,n;float p;
int main()
{ cin>>n;
for(i=3;i<=n;i++)
p=p+pow(i,2)/(i-2);
cout<<p;
return 0;}
Un tip de date este structurat dacă este format din tipuri de date elementare, omogen
sau nu.
Cel mai uzual tip stucturat de date este cel de tablou.
Alte tipuri structurate:
- matrici, masive;
- înregistrări, uniuni, fişiere etc.
Observaţii:
1. Generalizarea conceptului de vector poartă denumirea de tablou
(unidimensional = vector, bidimensional = matrice, tri/multi-dimensional = masiv)
Sintaxa:
<tip_vector> <nume_vector>[<dimensiune>]
Exemple: int vector[20]; char alfabet_RO[31];
2. Indexarea elementelor unui vector (ca şi a oricărui tablou în general) începe
în C/C++ de la 0.
3. C/C++ nu face “check boundaries” (nu verifică dacă s-a depăşit dimensiunea
vectorului) astfel că într-o declarare int v[2] folosirea valorii v[2] duce la un
comportament nedefinit al programului; acesta poate suferi un “crash” sau poate
returna o valoare necunoscută.
Eroare clasică:
int v[2] = {1, 3};// declarare vector cu initializare
cout << v[2];
Parcurgere:
Presupunem cunoscută dimensiunea n a vectorului.
Exemplu: for(i=0;i<n;i++)
<prelucrare elemente>
Unde <prelucrare elemente> poate fi:
a) citire:
cin>>vector[i];
b) afişare:
cout<<vector[i];
c) mixt a) şi b).
{cout<<”vector[“<<i<<”]=”;
cin>>vector[i];
cout<<endl;
d) alte prelucrări (vezi mai jos)
Prelucrări vectori
-aritmetice: sumă, produs scalar, norma unui vector etc.
-sortări: bubblesort, sortare cu pivotare, quick sort etc.
Metoda bulelor:
int bule=1;
while(bule = = 1)
30
{ bule=0;
for (i=1; i<=n; i++)
if (v[i] > v[i+1])
{aux= v[i]; // interschimb
v[i]= v[i+1];
v[i+1]= aux;
bule=1;
}
}
Metoda selecţiei directe (sortare cu pivotare)
printf("\n\nVectorul initial:\n");
for(i=0;i<n;i++)
printf("%6.2f",x[i]);
for(i=0;i<n-1;i++)
for(j=i+1;j<n;j++)
if(x[i]>x[j])
{ aux=x[i];
x[i]=x[j];
x[j]=aux;
}
-regăsirea unuia sau a mai multor elemente ale tabloului ce satisfac o sumă de
proprietaţi: regăsirea elementului care are o anumită valoare, determinarea minimului,
a maximului, verificarea dacă elementele vectorului determină o progresie, calculul
numărului de apariţii a unor anumite elemente (pare/impare, a celor divizibile printr-un
număr dat, a acelor elemente care sunt palindroame, a celor care conţin un anumit
număr de vocale etc.).
Exemplu: determinarea apariţiei sau nu într-un vector de 8 elemente a unei valori date
a.
int gasit=0, a=5;
for (i=0; i<=7&&!gasit; i++)
if (v[i]== a) {
cout<<”gasit”;
gasit=1; //determina parasirea buclei
}
if (!gasit)
cout<<”valoarea cautata nu se afla in vector”;
Eroare clasică:
int a = 5;
for (i=0; i<=7; i++)
if (v[i]== a)
cout<<”gasit”;
else
cout<<”negasit”;// va afisa pe rand gasit sau negasit, dupa
caz
-interclasarea: presupune obţinerea din 2 sau mai mulţi vectori sortaţi, un al treilea
format din elementele primilor, în aceeaşi ordine
(crescătoare sau descrescătoare).
while ((i<n) && (j<m))
if (a[i]< b[j])
c[k++]=a[i++];
else c[k++]=b[j++];
31
for (i=0; i<n; i++)
for (j=0; j<n; j++)
{cout << “ a[“ << i << “][“ << j << “]“;
cin>>a[i][j];
}
cout<<endl;
Dacă se doreşte iniţializarea elementelor unei matrici la declararea acesteia:
int m[2][3] = {
{1, -51, 23},
{-2, 7, 8} };
Prin această declarare, elementele matricii mat sunt iniţializate astfel:
mat[0][0]=1, mat[0][1]=-51, mat[0][2]=23
mat[1][0]=-2, mat[1][1]=7, mat[1][2]=8
▪ masive;
Declarare: <tip_masiv> nume_masiv[<dim1>][<dim2>]….[<dimn>]
}
void problema3() // produsul a 2 matrici, varianta, o puteti
imbunatati?
{
int n,i;
cout<<"N=";cin>>n;
int a[n];
for(i=0;i<n;i++)
{
32
cout<<"A["<<i+1<<"]=";cin>>a[i];
}
int m,mx;
cout<<"M=";cin>>m;
int b[m];
for(i=0;i<m;i++)
{
cout<<"B["<<i+1<<"]=";cin>>b[i];
}
float ps=0;
if(m==n) {
for(i=0;i<mx;i++)
ps=ps+a[i]*b[i];
}
else if(n>m)
{
for(i=0;i<m;i++) ps=ps+a[i]*b[i];
for(i=m;i<n;i++) ps=ps+a[i]*a[i];
}
else
{
for(i=0;i<n;i++) ps=ps+a[i]*b[i];
for(i=n;i<m;i++) ps=ps+b[i]*b[i];
}
cout<<ps;
}
void problema4 () // interschimbare valori vector de la margini
catre mijloc v=(1, 0, 5, 4) devine v= (4, 5, 0, 1). Putem citi
vectorul de la final spre inceput?
{
int aux, n,i;
cout<<"N=";cin>>n;
int v[n-1];
for(i=0;i<=n/2;i++)
{ aux=v[i]; v[i]=v[n-i-1];v[n-1-i]=aux;}
int main()
{
problema1();
problema2();
problema3();
problema4();
VI. Structuri
Obs. Spre deosebire de tablouri care conţin elemente de acelaşi tip, structurile/uniunile
pot conţine variabile diferite reunite sub acelaşi nume.
Structurile sau uniunile sunt tipuri structurate de date.
Variabilele ce alcătuiesc structura se numesc membrii structurii sau câmpuri. Pentru
accesul la un câmp a unei structuri se foloseşte operatorul “ . “ .
Declararea unei structuri se încheie cu “;“ deoarece e considerată o singură
instrucţiune.
Sintaxa:
struct<nume_structura>
{ <tip_camp_1><lista_var_1>;
<tip_camp_2><lista_var_2>;
.
33
.
.
<tip_camp_n><lista_var_n>; }
<lista_var_tip_struct>;
Structuri imbricate: în situaţii în care se declară o structură în interiorul unei alte
structuri.
Exemplu:
struct student {
char nume[30], prenume[30];
struct {
char specializare[20];
float note[14];
} Situatie;
}S1,S2;
Obs. În interiorul structurii “student” se află o structura anonimă ce poate fi apelată
prin intermediul variabilei “Situatie“:
cout<<S1.Situatie.specializare;
cout<<S2.Situatie.note[0];
Definiţie. Uniunile sunt un tip de dată structurată prin care toate câmpurile uniunii
partajează la un moment dat acceaşi locaţie (bloc) de memorie. Dacă o uniune conţine
mai multe câmpuri se alocă uniunii memorie egală cu maximul tipului câmpurilor
componente (cât pentru câmpul de tipul cel mai “mare”). Acest fapt permite ca aceeaşi
zonă de memorie să fie descrisă în mai multe moduri cum fiecare câmp al uniunii este
stocat în aceeaşi locaţie de memorie (de aici şi necesitatea alocării pentru o uniune a
maximului de memorie dintre câmpurile componente). Spre deosebire de uniune, unei
structuri i se alocă memorie egală cu suma câmpurilor componente (câmpurile având
fiecare propria locaţie). O uniune declarată în interiorul unei structuri descrie caracterul
“dinamic” al structurii.
Sintaxa:
union <nume_uniune>
Uniunile sunt în general declarate dacă există înregistrări cu structură variabilă. O
uniune poate fi declarată atât în afara unei structuri cât şi în interiorul ei.
Exemplu:
struct persoana {
char nume[20],prenume[20];
union {
int clase;
struct {
char denumire[20];
char oras[20]
} liceu;
struct {
char denumire[20], oras[20]
} facultate;
} studii;
} p;
p.studii.liceu.denumire sau
p.studii.clase sau
p.studii.facultate.oras
În exemplele de mai sus atribuirile nu pot fi folosite două simultan. Cum aceeaşi zonă
de memorie e partajată pentru toate câmpurile a doua alocare o va afecta pe prima.
Obs. În C structurile nu pot conţine funcţii spre deosebire de C++ unde este posibil.
34
Sintaxa: < tip >*< identificator_pointer >
Obs. Pointerii au o aritmetică proprie. O variabilă poate ocupa unul sau mai mulţi octeţi,
în funcţie de tipul său. Un pointer va conţine adresa unei variabile, în fond adresa
primului octet ocupat de aceasta. Dacă pentru o variabilă ştim adresa şi numărul de
octeţi ocupaţi, putem face un calcul prin care, de exemplu, să aflăm adresa următoare.
Procedeu dificil întrucât variabilele pot avea tipuri diferite. Aritmetica pointerilor
simplifică acest calcul:
Legătura pointeri–tablouri:
a) sir[3];
b) p=sir;
c) *(p+3); // vezi si operatii cu pointeri
Obs. p=sir ≡ p=&sir[20].
int b=5;
35
Obs. Deşi pentru declararea unei referinţe folosim operatorul &, tipul construcţiei în
care este folosit diferă.
Obs. O variabilă tip referinţă trebuie iniţializată obligatoriu. Similar un pointer constant
int *const p=&b;
Obs. O referinţă poate fi utilizată ca argument pentru o funcţie care poate să modifice
valoarea acestui argument.
Declaraţiile:
int a;
int *pa=&a;
şi:
int a;
int &ra=a;
atât pa cât şi ra au ca valoare adresa lui a. Atribuirea unei valori pentru a se poate face
fie &pa=3; fie ra=3; /* se Observa ca la folosirea pointerului adresa se
dereferentiaza folosind operatorul & in timp ce la referinta nu se
foloseste nimic */
36
Memoria alocată în heap poate fi deci reutilizată pe parcursul rulării programului.
Memoria heap nu este controlată direct de program ci independent prin funcţiile
sistemului de operare sau întreruperi. Obiectele sunt plasate în heap dar referinţele
către ele sunt pe stivă. Zona heap conţine valori reziduale de la folosirea ei anterioară.
Este o memorie care poate fi accesată prin dereferenţierea pointerilor şi nu direct ca
în cazul stivei. De aici necesitatea unui control atent al operaţiilor de alocare,
referenţiere şi dealocare în caz contrar în zona heap putând apărea frecevent scurgeri
de memorie (memory leaks). Folosirea lui „new” în locul unei declarări normale nu
este singura metodă de a muta o variabilă în heap- se mai poate face o variabilă locală
statică (domeniul său de vizibilitate rămâne local) sau globală însă oricine are acces
la ea.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
char text[20];
char *continut;
if(continut == NULL ) {
fprintf(stderr, "Error - unable to allocate required
memory\n");
}
else {
strcpy(continut, "Ziua buna se cunoaste de dimineata!");
}
if(continut == NULL ) {
fprintf(stderr, "Error - unable to allocate required
memory\n");
}
else {
strcat(continut, "Doar daca nu ai ore de la 8.00!");
}
37
free(continut);
}
Se afişează:
Buna ziua!
Ziua buna se cunoaste de dimineată! Doar daca nu ai ore de la
8.00!
Obs:
2. void *calloc(int nr, int size); // aloca un vector de nr de elemente, fiecare de mărime size
3. void *realloc(void *adresa, int newsize); // realoca memoria extinzand-o pana la newsize
VIII. Subprograme
În general un subprogram este un modul scris independent de programul principal, dar
legat de acesta printr-un proces de transfer şi revenire.
<tip_rezultat><nume_subprog>(<lista_param_formali>)
Exemplu: int maxim(int a,int b)
bool este_in_sir(double sir[], double x )
void aria(float lung, float latime)
Obs. Spre deosebire de Pascal unde există chiar cuvântul rezervat procedure, în
C/C++ nu există proceduri, ci doar funcţii către void (funcţii care nu întorc nici un
rezultat). Spre deosebire de funcţii, procedurile (în sens clasic) nu întorc în modulul
apelant nici o valoare (rezultat).
Obs. Funcţiile pot conţine minim o singură dată o instrucţiune return. La întânirea
acesteia se părăseşte funcţia cedându-se controlul modulului apelant.
38
Obs. În general entităţile (uzual variabile) declarate în interiorul unui subprogram sunt
entităţi locale – pot fi utilizate doar în interiorul subprogramului respectiv. În cazul în
care unele entităţi trebuie folosite în comun de toate modulele (sau doar de o parte)
unui program, acestea se vor numi entităţi globale.
1) Datele sunt copiate efectiv din locaţiile respective, din programul principal, în
celulele de memorie asociate subprogramului, caz în care se spune că
transferul se realizează prin valoare (ineficient dacă dimensiunea datelor
asociate parametrilor sunt mari, întrucât, lucrându-se cu copii ale parametrilor
reali, stiva alocată subprogramului se încarcă).
Obs. La apel, parametrii formali sunt substituiţi cu cei reali, cele două liste trebuie să
coincidă ca tip şi număr de parametri.
Exemplu:
int x, y, z;
void P(int x, int &y)
{z = x + y;
x++;
y = y + x;
cout<< x<<y<<z<<endl;}
void main()
{
x = 3; z = 4; int t = 10;
P(t,x);
cout<<x<<y<<z;}
39
{
x++; y = x;
}
void main()
{
int x = 0, y;
while(x<3)
{
p(x ,y);
x++;
cout<<x<<y<<endl;
}
x=0;
while(x<3)
{
pa(x ,y); x++; cout<<x<<y<<endl;
}
}
Obs. Dacă mediul nu acceptă tipul logic (bool), bool se înlocuieşte cu integer, true←1,
false←0.
#include<stdio.h>
#include<stdlib.h>
void citeste(int x[], int &n, char nume[])
{int i;
printf(“Dati numarul de elemente pentru vectorul %s”, nume);
scanf(“%d”, &n);
for(i=1; i<=n; i++)
{printf(“Dati %s[%d]= “, nume, i);
scanf(“%d”, &(x[i]));
}
}
void afiseaza(int x[], int n, char nume[])
{int i;
printf(“Vectorul este %s: “, nume)
for(i=1; i<=n; i++)
{printf(“%d”, x[i]);
printf(“\n”);
}
}
bool estein(int a ,int x[], int n)
{int i;
for(i=1; i<=n; i++)
if(x[i] == a)
return true;
return false;
}
void multime(int x[], int n, int y[], int &m)
{int i;
m = 0;
for(i=1; i<=n; i++)
if(!estein(x[i], y, m)
{m++;
y[m] = x[i];
40
}
}
void reuniune(int x1[], int n1, int x[], int n2, int r[], int &nr)
{int i;
for(i=1; i<=n1; i++)
r[i] = x1[i];
nr = n1;
for(i=1; i<=n2; i++)
if(!estein(x2[i], x1, n1)
{nr++;
r[nr] = x2[i];
}
}
void intersectie(int x1[], intn1, int x2[], int n2, int s[], int
&ns)
{int i; ns = 0;
for(i=1; i<=n1; i++)
if(estein(x1[i], x2, n2)
{ns++;
s[ns] = x1[i]; }}
int main()
{int a[20], b[20], am[20], bm[20], reun[40], inters[20],
difer[20];
int na, nb, nam, nbm, nr, ns, nd;
citeste(a , na, “a“);
afiseaza(a, na, “a”);
citeste(b, nb, “b“);
afiseaza(b, nb, “b“);
multime(a, na, am, nam);
afiseaza(am ,nam, “Multime a“);
multime(b ,nb ,bm ,nbm);
afiseaza(bm ,nbm, “Multime b“);
reuniune(am ,nam ,bm , nbm, reun, nr);
afiseaza(reun , nr, “Reuniunea: “);
intersectie(am, nam, bm, nbm, inters, ns);
afiseaza(inters, ns, “Intersectia: “);
diferenta(am, nam, bm, nbm, difer, nd);
afiseaza(difer, nd, “Diferenta: “);
………………
}
Obs. Transferul prin adresă se poate face şi prin intermediul pointerilor.
Exemplu:
Obs. Pointeri la structuri: când structurile sunt mai mari, este mai eficient să se
transmită un pointer către acea structură, iar în interiorul funcţiei să se folosească
accesul prin pointer. Când un pointer al unei structuri e transmis unei funcţii, în stivă
se reţine doar adresa structurii. Acest lucru garantează apeluri de funcţii rapide,
funcţiile putând totodată să modifice conţinutul structurii folosite la apelare.
Exemplu:
41
struct timp
{int ora;
int minute;
int secunde;
}*t;
- apel câmp structură prin pointer
if(t→minute == 60)
{t→minute = 0;
t→ora++;
}
- funcţie simulare potrivire oră la ceas
Deşi funcţia nu este o variabilă, ea are localizare în memorie; aceasta poate fi atribuită
unui pointer. Altfel spus, adresa unei funcţii este punct de intrare în funcţie, deci se
poate folosi un pointer către o funcţie pentru a o apela.
Obs. Adresa unei funcţii se obţine specificând numele funcţiei fără paranteze sau
argumente.
Exemplu:
void main()
{float a = 2, b = 3, c;
float(*t[3])(float, float);
t[0] = mult;
t[1] = add;
t[2] = scad;
c = (*t[1])(a, b);
c = operatie(scad, a, b);
IX. Recursivitate
Prin funcție recursivă se înțelege o funcție care se autoapelează, în general cu alt
parametru. Condițiile ca o problemă să se poată rezolva recursiv le găsiți, de exemplu
în resursa [7]. Recursivitatea poate fi directă sau indirectă (determinarea valorilor unei
funcții prin apelul alteia).
42
Obs. Posibilitatea de a rezolva o problema recursiv este o caracteristică a limbajului
de programare şi nu ţine doar de formularea problemei.
I. Mod iterativ:
Exemplu:
#include<iostream>
using namespace std;
void frec(int n)
{if(n == 0)
{cout<<”GATA”<<endl;
return;}
cout<<”#n”<<n<<” “<<$n<<endl;
frec(n-1);
cout<<”*n”<<n<<” “<<$n<<endl;
return;}
void main()
{frec(5);…}
Se afişează : #n 5 0012FE98
#n 4 0031DE45
#n 3 00120AB20
#n 2 0011AB19
#n 1 0011AB15
GATA
*n 1 0011AB15
*n 2 0011AB19
. . . . . . .
*n 5 0012FE98
{if(n == 1)
return 1;
else
return (1+Peano(n-1));
}
43
Obs. Reamintim: f(1)=1 (funcţia identitate), f(n)=f(n-1)+1.
c) Dacă apelăm funcţia cu o valoare foarte mare putem obţine eroare generată
de depăşirea spaţiului de memorie.
Exemplu:
Peano(214748364) //OK
Peano(412400012) //eroare
2) Combinări
R) int combr(int n, int k)
{if((n == k)||(k == 0))
return 1;
else
return (combr(n-1, k-1) + combr(n-1, k ));}
Aplicații:
1. Fibonacci
#include <iostream>
int main()
{
int n;
cin>>n;
cout<<fib(n);
return 0;
}
45
return 0;
}
4. Exemplu recursivitate indirectă
using namespace std;
int an(int n)
{
if(n>0)
{
return bn(n-1);
}
else return 1;
}
int bn(int n)
{
if(n>0)
{
return an(n-1);
}
else return 0;
}
int main()
{
int n,r;
cin>>n;
cout<<"an[1] sau bn[2] ?"<<endl;
cin>>r;
if(r==1) cout<<an(n);
else if(r==2) cout<<bn(n);
return 0;}
X. Șiruri de caractere
Limbajul C nu defineste tipul de data sir, dar exista două posibilități de definire a
șirurilor:
a) ca tablou de caractere;
▪ char sir1[30];
▪ char sir2[15]="exemplul doi";
46
Index 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14
Conținut e x e m p l u l d o i \0 - -
b) ca pointer la caractere;
• declarate in stdio.h
3) scanf("%s",s);
4) printf("%s",s);
tipărește șirul s
• declarate in string.h
47
5) int strcmp(char *s1,char *s2);
0, daca s1 = s2
>0, daca s1 > s2
#include <stdio.h>
#include <string.h>
int main (void)
{ char sir [80]; int k, n;
printf ("Introduceti sirul: ");
gets (sir); n = strlen (sir);
for (k = n - 1; k >= 0; k--)
printf ("%c", sir [k]);
48
printf ("\n"); return 0; }
#include <stdio.h>
#include <string.h>
int main (void)
{ char sir [80]; int k, n, val, p, cifra, valid;
do
{ do {
printf ("Introduceti sirul: ");
gets (sir); n = strlen (sir); }
while (n = = 0);
valid = 1;
for (k = 0; k < n; k++)
if ((sir [k] != '0') && (sir [k] != '1')) valid = 0;
if (!valid)
printf ("Sirul introdus nu este numar in baza 2 ! \n"); }
while (!valid);
p = 1; /* puterea lui 2 */
val = 0; /* valoarea numarului */
for (k = n - 1; k >= 0; k--)
{ cifra = sir [k] – '0'; val = val + cifra * p; p = p * 2; }
printf ("Numarul %s in baza 10 este: %d \n",sir,val); return 0; }
#include <stdio.h>
#include <string.h>
#include <conio.h>
#include <ctype.h>
char tab_cuv[NR][LUNG];
int nr_cuv=0; // numarul cuvintelor introduse
void citire_cuv(void){
printf("*** Se introduc maxim %d cuvinte, terminate cu
CTRL/Z:\n",NR);
while(nr_cuv<NR && gets(tab_cuv[nr_cuv]))nr_cuv++;
/* la CTRL/Z gets returneaza NULL (= 0) */
/* citirea se poate face si cu scanf:
while(nr_cuv<NR &&
scanf("%s",tab_cuv[nr_cuv])!=EOF)nr_cuv++; */
49
/* daca terminarea se face cu un cuvant vid:
while(nr_cuv<NR &&
strcmp("",gets(tab_cuv[nr_cuv])))nr_cuv++; */
}
#include <alloc.h>
char* tab_cuv[NR];
void citire_cuv(void){
char cuv_crt[LUNG];
printf("*** Se introduc maxim %d cuvinte, terminate cu
CTRL/Z:\n",NR);
while(nr_cuv<NR && gets(cuv_crt)){
if((tab_cuv[nr_cuv]=(char
*)malloc(strlen(cuv_crt)+1))==NULL){
puts("*** Spatiu insuficient ! ***");
exit(1);
}
strcpy(tab_cuv[nr_cuv],cuv_crt);
nr_cuv++;
}
}
*/
void constr_cuv(void){
char c,cuv[NR+1]; /* lungimea maxima este egala cu numarul
maxim de cuvinte + 1 pentru terminator */
int i,j,lung; /* i parcurge cuvintele din tab_cuv, j
pozitiile din cuv, lung e lungimea cuv curent */
for(i=j=0;i<nr_cuv;i++)
if((lung=strlen(tab_cuv[i]))>0 &&
isalpha(c=tolower(tab_cuv[i][lung-1])))
cuv[j++]=c;
cuv[j]='\0';
printf("*** Cuvantul construit:|%s|\n",cuv);
}
void cuv_max(void){
int i,lung_crt,lung_max=0;
char * p_max; /* pointerul spre cuvantul maxim */
/* se poate memora indicele cuvantului maxim: int i_max;
sau memora cuvantul maxim intr-un sir: char
c_max[LUNG]; */
for(i=0;i<nr_cuv;i++)
if((lung_crt=strlen(tab_cuv[i]))>lung_max){
p_max=tab_cuv[i];
lung_max=lung_crt;
}
printf("*** Cuvantul de lungime maxima %d
este:|%s|\n",lung_max,p_max);
}
50
void cuv_vocale(void){
int i;
puts("*** Cuvintele ce incep cu vocale:");
for(i=0;i<nr_cuv;i++)
switch(toupper(tab_cuv[i][0])){
case 'A': case'E': case 'I': case 'O': case
'U':puts(tab_cuv[i]);
} /* in loc de switch se putea folosi
char c; if(c=toupper(tab_cuv[i][0]),c=='A' ||
c=='E' || ...)puts(tab_cuv[i]); */
}
void main(void){
citire_cuv();
cuv_max();
constr_cuv();
cuv_vocale();
getch();
}
Se citesc trei siruri s1, s2 si s3. Sa se afiseze sirul obtinut prin inlocuirea in s1 a
tuturor aparitiilor lui s2 prin s3. (Observatie: Daca s3 este sirul vid, din s1 se vor
sterge toate subsirurile s2).
#include <stdio.h>
#include <string.h>
#define LUNGS 81
void main(void){
char s1[LUNGS],s2[LUNGS],s3[LUNGS],rezultat[LUNGS];
char *s1ptr=s1,*s2pos, *rezptr=rezultat;
while(s2pos=strstr(s1ptr,s2)){
while(s1ptr<s2pos)*rezptr++=*s1ptr++;
strcpy(rezptr,s3);
rezptr+=strlen(s3);
s1ptr+=strlen(s2);
}
strcpy(rezptr,s1ptr);
puts("*** sirul rezultat:"); puts(rezultat);
getch();
}
51
4. Pornind de la un sir citit de la tastatura, sa se construiasca si sa se tipareasca
#include <stdio.h>
#include <conio.h>
#include <string.h>
#include <ctype.h>
#define LUNGS 81
//variabile globale
char sir[LUNGS],aux[LUNGS];//sirul citit si cel in care se fac
prelucrarile
//prototipuri
void constr1(void);
void constr2(void);
void constr3(void);
//definire main
void main(void){
clrscr();
puts("*** sirul de prelucrat:"); gets(sir);
constr1();
constr2();
constr3();
getch();
}//main
//definire functii
void constr1(void){
int i,j; //i indice in sir, j in aux
for (i=j=0;i<strlen(sir);i++) // expresia conditie poate fi
sir[i]
if(isdigit(sir[i])) // conditia echivalenta
cu '0'<=sir[i]<='9'
aux[j++]=sir[i];
aux[j]='\0';
printf("Sirul de cifre:%s\n",aux);
}
void constr2(void){
int i,j;
char c;
for (i=j=0;i<strlen(sir);i++)
if(c=tolower(sir[i]),c=='a'||c=='e'||c=='i'||c=='o'||c=='u
')
aux[j++]=c;
52
aux[j]='\0';
printf("Sirul de vocale:%s\n",aux);
}
void constr3(void){
int i,lung; //lung ia valoarea lungimii sirului
for (i=0,lung=strlen(sir);i<lung;i++)
aux[i]=sir[lung-1-i];
aux[i]='\0';
printf("Sirul invers:%s\n",aux);
Fişierul are un articol care marchează sfârşitul fişierului. Pentru fişierul standard de
intrare de la tastatură, sfârşitul de fişier, pentru sistemele de operare DOS şi Windows se
generează prin Ctrl-Z (pentru Unix – prin Ctrl-D). Vezi si EOF
nivelul superior, care utilizează structuri speciale FILE (structuri de date, FILE *pf; )
Într-un fişier text, toate datele sunt memorate ca şiruri de caractere, organizate pe linii,
separate între ele prin marcajul sfârşit de linie ‘\n’. (EOL=<E>: <CR>+<LF=N>)
Într-un fişier text spaţiul de memorare pe disc nu este folosit în mod eficient pentru
datele numerice (astfel întregul 12345 ocupă 5 octeţi).
Într-un fişier binar, datele sunt păstrate în formatul lor intern (2 octeţi pentru int, 4
octeţi pentru float, etc).
La fişierele text marcajul de sfârşit de fişier (caracterul 0X1A) există fizic în fişier. La
întâlnirea acestui caracter funcţia fgetc() întoarce EOF (-1). Marcajul de sfârşit de fişier
se generează de la tastatură prin Ctrl-Z.
În cazul fişierelor binare, marcajul de sfârşit de fişier nu există fizic în fişier, ci este generat
de funcţia fgetc().
În MSDOS (şi în Unix), la nivelul liniei de comandă intrările şi ieşirile standard pot fi
redirectate în fişiere disc, fără a opera nici o modificare la nivelul programului. Astfel:
cin>>fisier;
54
NUL – pentru perifericul nul.
Exemple:
> test.exe < f1.dat > f2.dat redirectează atât intrarea cât şi ieşirea
programului
3. Accesul la fişiere.
FILE *pf;
Prin deschiderea unui fişier se stabileşte o conexiune logică între fişier şi variabila pointer şi
se alocă o zonă de memorie (buffer) pentru realizarea mai eficientă a operaţiilor de intrare /
ieşire. Funcţia întoarce:
citire (sau consultare) “r” – citirea dintr-un fişier inexistent va genera eroare
55
// flux=fisier=buffer
Fişierul deschis este închis şi este deschis un nou fişier având ca sursă fluxul, numele şi
modul de acces specificaţi ca parametri. O utilizare importantă o constituie redirectarea
fluxului standard de intrare: datele vor fi citite din fişierul specificat, fără a face nici o
modificare în program.
fisier>>citesc_ceva; fisier<<scriu_ceva
După terminarea prelucrărilor asupra unui fişier, acesta trebuie închis. Un fişier este închis
automat la apelarea funcţiei exit().
în cazul unui fişier de ieşire, se scriu datele rămase nescrise din buffer în fişier, aşa că
operaţia de închidere este obligatorie
în cazul unui fişier de intrare, datele necitite din bufferul de intrare sunt abandonate
fclose(pf);
Funcţia întoarce ca rezultat următorul caracter citit din fişier, convertit în întreg fără
semn sau EOF dacă s-a citit sfârşit de fişier sau s-a produs o eroare la citire.
Scrieţi un program care realizează copierea unui fişier. Numele celor doua fişiere (sursă şi
destinaţie) sunt citite de la terminal.
#include <stdio.h>
void main(void){
gets(numes);
gets(numed);
FILE* s = fopen(numes,”r”);
FILE* d = fopen(numed,”w”);
copiere1(d, s);
fclose(s);
fclose(d);
int c;
fputc(c, d);
citeşte caractere din fişierul cu pointerul pf, până la întâlnirea primului caracter ‘\n’ (cel
mult n-1 caractere) în tabloul s; pune la sfârşit ‘\n’ şi ‘\0’
57
Scrieţi o funcţie care simulează funcţia fgets().
char c;
char *psir = s;
if((*psir++=c)==’\n’)
break;
*psir=’\0’;
întoarce un rezultat nenegativ (numărul de caractere scrise în fişier), sau EOF la producerea
unei erori
{ int c, n=0;
while (c = *s++){
fputc(c, pf);
n++;
Copierea unui fişier folosind funcţii orientate pe şiruri de caractere are forma:
58
#define MAX 100
char linie[MAX];
fputs(linie, d);
Revenim acum asupra modurilor de acces la disc. Sunt posibile următoarele situaţii:
“w” - deschidere pentru scriere, noile scrieri se fac peste cele vechi
fişierul există deja; dorim să adăugăm informaţii la el, păstrând informaţiile deja existente
fişierul există deja; dorim să punem alte informaţii în el ştergând pe cele existente
Modul de acces binar se specifică cu sufixul “b”. Astfel avem: “rb”, “w+b”
Modul text este considerat implicit, dar poate fi specificat explicit prin “t”.
scrierea cu format
întoarce numărul de caractere scrise, sau o valoare negativă, dacă s-a produs o eroare.
Un descriptor de conversie din format începe prin % şi poate avea un mai mulţi specificatori
opţionali, care preced descriptorul:
%[indicator][lăţime][.precizie][spec_lung]descriptor;
%#x - scrie 0x
citirea cu format
se citesc date din fişierul pf, sub controlul formatului, iniţializându-se variabilele din
listă
funcţia întoarce numărul de câmpuri citite sau EOF în caz de producere a unui
incident la citire sau întâlnire a marcajului de sfârşit de fişier.
întoarce numărul de înregistrări citite sau 0 în caz de eroare sau sfârşit de fişier
Pentru a copia un fişier binar (sau text) folosind funcţiile fread() şi fwrite() vom
considera lungimea articolului 1 octet.
char zona[MAX];
modifică poziţia curentă în fişierul cu pointerul pf cu depl octeţi relativ la cel de-al
treilea parametru orig, după cum urmează:
întoarce poziţia curentă în fişier, exprimată prin numărul de octeţi faţă de începutul
fişierului
61
#include <stdio.h>
return noct;
Tratarea erorilor
Se vor crea de asemeni două fişiere, unul cu comenzi care nu au fost satisfăcute,
deoarece produsele erau în cantităţi insuficiente, celălalt cu comenzi de produse care
nu există în depozit.
62
#include <stdio.h>
#include <stdlib.h>
void main(void)
{ comanda com;
depozit dep;
factura fac;
double t;
/* deschidere fisiere */
if((fc=fopen(“comenzi.dat”,”rb”))==NULL){
exit(1);
};
if((fd=fopen(“depozit.dat”,”r+b”))==NULL){
exit(1);
};
ff=fopen(“facturi.dat”,”wb”);
fc1=fopen(“com1.dat”,”wb”);
fc2=fopen(“com2.dat”,”wb”);
63
if(!fread(&com, sizeof(com), 1, fc) break;
gasit=0;
rewind(fd);
do {
if(!eof) {
if(strcmp(com.den, dep.den)==0){
gasit = 1;
if((t=dep.stoc – com.cant)>=dep.stoc_min) {
dep.stoc=t;
fac.val=com.cant * dep.pret;
strcpy(fac.den, com.den);
else
break;
if(!gasit)
fclose(fc);
fclose(fd);
fclose(ff);
fclose(fc1);
64
fclose(fc2);
Semnătură Efect
int fscanf(FILE* pf, char* format, citire din fişier sub controlul
formatului. Întoarce numărul de
lista_adrese); câmpuri citite sau EOF, în caz de
eroare sau sfârşit de fişier.
int fread(char* zona, int la, citeşte din fişier în zona, na articole
de lungime la fiecare.Întoarce
int na, FILE* pf); numărul de articole efectiv citite.
int fwrite(char* zona, int la, scrie în fişier din zona, na articole de
lungime la fiecare.Întoarce numărul
int na, FILE* pf); de articole efectiv scrise.
65
long ftell(FILE* pf); determină poziţia curentă în fişier.
Fişiere în C++
FILE *pf=open(„cale://nume”...);// in C
În instrucţiunea cout << x; cout este un obiect de tip flux de ieşire standard
(monitorul). Transferul informaţiei în fişier se realizează prin intermediul operatorului <<
supraîncărcat (operator pe biti, deplasare stanga: 111(7)->11(3)).
cin este obiectul flux de intrare standard; transferul informaţiei se face prin operatorul
supraîncărcat >>.
Operații cu fluxuri
Deschiderea unui fişier (asocierea fişier – stream) se face cu funcţia membru open(), avînd
semnătura:
66
void open(char* numefisier, int mod, int acces);
Modul în care poate fi deschis mod un fişier este precizat în clasa ios prin
enumeratorii:
trunc - dacă fişierul există, va fi şters şi se va crea un fişier nou pentru scriere
binary - fişerul deschis va fi prelucrat ca un fişier binar (pt fisiere cu continut altul
decat text)
ifstream f;
2 – fişier ascuns
4 – fişier sistem
8 – fişier arhivă
Obs. Dacă în urma deschiderii fişierului, variabila stream este egală cu NULL, atunci
operaţia de deschidere a eşuat.
ifstream f;
if(!f.open(”note.dat”, ios::in)){
exit(-1);
67
}
Obs. Deschiderea fişierului se poate face implicit, fără a folosi funcţia open(),
dacă parametrii sunt specificaţi la declararea obiectului fişier:
Semnătura Efect
istream& get(char* sir, int n, se citesc cel mult n-1 caractere, sau până
la întâlnirea separatorului sep. Separatorul
char sep=’\n’);
este pus în şir.
istream& getline(char* sir, se citesc cel mult n-1 caractere, sau până
la întâlnirea separatorului sep. În locul
int n, char
separatorului este pus terminatorul de şir
sep=’\n’);
‘\0’.
istream& read(char* sir, int citeşte în mod binar cel mult n caractere
n);
68
ostream& write(const char* scrie cel mult n caractere în fluxul de ieşire
sir,
int n);
Exemplu:
#include <fstream.h>
void main(){
ofstream out;
ifstream in;
out.open(“iesire.dat”);
out.close();
in.open(“intrare.dat”);
in >> linie;
in.close();
#include <stdio.h>
#include <conio.h>
int main()
{
char ch, s[100], nume_fis[50]="D:\\fisier1.txt";
int i;
69
FILE *pf;
// crearea fişierului; scrierea caracterelor inclusiv '\n'
introduse de la tastatura
pf=fopen(nume_fis,"w");
printf("Introduceti textul. Pentru a termina apasati CTRL+Z si
ENTER \n");
while ((ch=getc(stdin))!=EOF)
{
putc(ch,pf);
}
fclose(pf);
/*Adaugarea de siruri de caractere*/
pf=fopen(nume_fis,"r+");
fseek(pf,0l,SEEK_END);
printf("Introduceti sirurile de caractere terminate cu ENTER.
Pentru a termina\
apasati CTRL+Z si ENTER\n");
while(fgets(s,100,stdin)!=(char*)0)
{
fputs(s,pf);
}
fclose(pf);
/*Afisarea continutului */
printf("CONTINUTUL FISIERULUI cu NUMEROTAREA LINIILOR:\n");
i=0;
pf=fopen(nume_fis,"r");
while(fgets(s,100,pf)!=(char *)0)
{
printf("%d %s", i, s);
i++;
}
fclose(pf);
printf("Dupa apasarea unei taste fisierul va fi sters!\n");
getch();
remove(nume_fis);
return 0;
}
#include <stdio.h>
/* Programul ilustreaza prelucrarea binara a unui fisier */
typedef struct {
char nume[40];
long suma;
} ARTICOL;
int main()
{
FILE *pf;
ARTICOL buf;
int i,n;
char nume_fis[40]="C:\\fisier2.dat";
/*Crearea fisierului */
printf("Introduceti numarul de persoane: ");
scanf("%d",&n);
pf=fopen(nume_fis,"wb");
for(i=1;i<=n;i++)
{
fflush(stdin);
printf("Numele persoanei: ");
fgets(buf.nume,40,stdin);
printf("Suma = ");
scanf("%ld",&buf.suma);
fwrite(&buf,sizeof(ARTICOL),1,pf);
}
fclose(pf);
printf("\nCONTINUTUL FISIERULUI\n");
afisare(nume_fis);
return 0;
}
Nr. 1.
I. Structuri de control fundamentale
II. Metoda bulelor (secvența de cod)
III. Scrieți o funcție recursivă pentru calculul sumei unei progresii aritmetice
IV. Scrieti codul prin care un fișier text sursa.dat este copiat în invers.dat, în
ordine inversă a liniilor
Nr. 2
I. Structuri de control derivate
II. Sortare cu pivotare
III. Scrieți o funcție recursivă pentru calculul sumei unei progresii geometrice
IV. Scrieti codul prin care un fișier text sursa.dat este copiat în invers.dat,
fiecare linie din primul regăsindu-se în ordine inversă în cel de al doilea.
71
Resurse disponibile online
[1]. https://ro.scribd.com/document/366479012/02-Algoritmi
[2]. http://users.utcluj.ro/~igiosan/Resources/PC/Lab/L01.pdf
[3]. http://www.scritub.com/stiinta/informatica/Etapele-realizarii-unui-
progra184261513.php
[4]. http://www.asciitable.com
[5]. http://www.cplusplus.com/doc/tutorial/operators/
[6]. http://thor.info.uaic.ro/~pc/pcII/stilC++/index.html#top
[7]. http://infoscience.3x.ro/c++/recursivitate.htm
[8]. https://info64.ro/Recursivitate/
[9]. http://users.utcluj.ro/~igiosan/Resources/PC/Lab/L11.pdf
Bibliografie
1. Andone R., Gârbacea I., Algoritmi fundamentali o perspectivă C++, Editura Libris,
Cluj Napoca, 1995.
2. Calude C., Complexitatea calcului, aspecte calitative, Editura Ştiinţifică şi
Enciclopedică, Bucureşti, 1982.
3. Cormen T.H., Leiserson E.C., Rivest R.R., Introducere în algoritmi, Editura Libris
Agora, 2000 (traducere în limba română).
4. Dahl O.J., Dijkstra E.W., Hoare C.A.R., Structured Programing, Academic Press,
1972.
5. Frenţiu M., Motogna S., Lazăr I., Prejmerean V., Elaborarea algoritmilor, Litografia
Universităţii “Babeş Bolyai”, Cluj Napoca, 1998.
6. Knuth E. Donald, Arta programării calculatoarelor, orice editie.
7. Lica D., Onea E., Informatică, Manual pentru clasa a IX-a, Editura L&S, Bucureşti,
1999.
8. Livovschi L., Georgescu H., Sinteza şi analiza algoritmilor, Editura Ştiinţifică şi
Enciclopedică, Bucureşti, 1986.
9. E. Horowitz, S. Sahni, "Fundamentals of Computer Algorithms" – 1985 ca şi
"Fundamentals of Data Structures".
10. McConnell S.C., Code Complete: a practical handbook of software construction,
Microsoft Press, Washington, 1993
11. Motogna S.,Metode şi tehnici de proiectare a algoritmilor, Universitatea “Babeş
Bolyai”, Cluj Napoca, 1998/
12. Tudor S., Cerchez E., Şerban M., Informatică. Manual pentru clasa a IX-a, Editura
L&S, Bucureşti, 1999 (orice manual de liceu de C/C++ e util)
13. R. Vişinescu, V. Vişinescu- Algoritmi şi structuri de date- Teorie şi aplicaţii.
Probleme de concurs, Editura Edusoft, 2006.
72