Documente Academic
Documente Profesional
Documente Cultură
Să se scrie un program
care să calculeze și să afișeze aria și perimetrul cercului de rază R. Afișarea rezultatului să se
facă cu aproximare la 2 zecimale.
Varianta 1: aproximarea la 2 zecimale in momentul in care se calculeaza aria si perimetrul
import math
R = float(input("R="))
P = round(2*math.pi*R,2)
A = round(math.pi*R*R,2)
print("Perimetrul =", P)
print("Aria =" , A)
1
print("c.m.m.d.c.=",i)
else:
d=i
i=r
4. Să se scrie un program ce calculează coordonatele de intersecție a două drepte date de
ecuațiile:
𝑦 = 𝑚1𝑥 + 𝑛1; 𝑦 = 𝑚2𝑥 + 𝑛2;
m1 = float(input ("m1="))
n1 = float(input("n1="))
m2 = float(input ("m2="))
n2 = float(input("n2="))
if m1 == m2:
if n1 == n2:
print("Dreptele coincid! \n")
else:
print("Drepte paralele! \n")
else:
x = round((n2 - n1)/(m1 - m2),2)
y = round(m1*x + n1,2)
print("Punctul de intersectie este (", x, ",", y, ")")
5. Să se scrie un program ce rezolvă ecuația de gradul 2: 𝑎𝑥2+𝑏𝑥+𝑐=0, unde a, b și c sunt
numere reale.
import math
a = float(input ("a="))
b = float(input("b="))
c = float(input ("c="))
if a != 0:
d = b*b - 4*a*c;
if d > 0:
x1 = (-b + math.sqrt(d))/(2*a);
2
x2 = (-b - math.sqrt(d))/(2*a);
print("x1 =", x1) print("x2 =", x2)
else:
if d == 0:
x = -b/(2*a)
print("x1 = x2 =", x)
else:
coefr = -b/(2*a)
imag = math.fabs(math.sqrt(-d)/(2*a))
print("coefr =", coefr)
print("imag =", imag)
else:
if b != 0:
x = -c/b
print("x = ", x)
else:
if c != 0:
print("Ecuatie imposibila!\n");
else:
print("Ecuatie nedeterminata!\n");
6. Să se scrie un program care citește de la tastatură lungimea laturii unui triunghi echilateral și
afișează pe ecran înălțimea triunghiului și aria sa, pe linii diferite, însoțite de mesaje
explicative. (3)
import math
l=float(input("l="))
h=round((l*math.sqrt(3))/2)
A=round((l*l*math.sqrt(3))/4)
print ("Inaltimea triunhiului este",h)
print ("Aria triunghiului este",A)
3
7. Un melc parcurge o distanță de D metri în H ore. Să se scrie un program care să calculeze și
să afișeze viteza cu care se deplasează melcul (exprimată în metri/secundă). (3)
import math
#D=distanta in m, H=timpul in ore, h=timpul in secunde, v=viteza
D=float(input("D="))
H=float(input("H="))
h=H*3600
v=D/h
print ("Viteza melcului este",v)
import math
x=float(input("x="))
if x<=10:
print("f=",round(math.sqrt(abs(2-math.pow(x,2)))))
else:
print((-2*x+1)/3)
9. Fie c o variabilă de tip char. Scrieți o expresie care, în cazul în care valoarea variabilei c este
o literă mică, atribuie variabilei c majuscula corespunzătoare. (4)
c=str(input("Introdu litera: "))
print(c.upper())
10. Fie A și B două puncte în plan, specificate prin coordonatele lor carteziene. Să se scrie un
program care să calculeze și să afișeze lungimea segmentului AB. (4)
import math
4
print("Introduceti coordonatele punctului A: ")
xA=float(input("Introduceti valoarea lui xA: "))
yA=float(input("Introduceti valoarea lui yA: "))
print("Introduceti coordonatele punctului B: ")
xB=float(input("Introduceti valoarea lui xB: "))
yB=float(input("Introduceti valoarea lui yB: "))
print("Lungimea segmentului este:")
AB=print(math.sqrt(pow(xB-xA,2)+(pow(yB-yA,2))))
11. Fie x un număr natural. Scrieți o expresie care să utilizeze operatori logici pe biți astfel încât:
(4)
a. să înmulțească valoarea variabilei x cu 2n (0≤n≤15);
b. să împartă valoarea variabilei x cu 2n (0≤n≤15).
REZOLVARE:
a.)
x=int(input("Introdu un numar: "))
n=int(input("Introdu un alt numar: "))
s=x*2*n
print("Valoarea expresiei calculate anterior este: ", s)
b.)
x=int(input("Introdu un numar: "))
n=int(input("Introdu un alt numar: "))
s=x/(2*n)
print("Valoarea expresiei calculate anterior este: ", s)
12. Se citește un număr zecimal și unul în hexa de cel mult 4 cifre fiecare. Se cere să se afișeze
valorile respective în sistemele de numerotație cu baza 10, 8 si 16. (4)
zecimal=int(input("Nr. zecimal: "))
hexa=str(input("Nr.hexazecimal:"))
5
print(oct(zecimal))
print(hex(zecimal))
print(hexa)
print(oct(int(hexa,base=16)))
print(hex(int(hexa,base=16)))
13. Fie ecuația cu coeficienți reali 𝑎𝑥2+𝑏𝑥+𝑐=0 (𝑎≠0). Scrieți un program care fără a calcula
rădăcinile ecuației, să determine natura și semnul acestora. (5)
a=int(input("Introduceti a="))
b=int(input("Introduceti b="))
c=int(input("Introduceti c="))
S=(-b)/a
P=c/a
if P>0:
if S>0:
print ("x1>0, x2>0")
else:
print ("x1<0, x2<0")
else:
if S>0:
print ("x1<0, x2>0, |x1|<x2")
else:
print ("x1<0, x2>0, |x1|>x2")
if S==0:
print("rădăcinile sunt egale în modul și de semne contrare: x1=-x2")
if P==0:
print("una dintre rădăcini este zero: x1=0 sau x2=0")
14. Două numere naturale a și b se numesc prietene dacă a este egal cu suma divizorilor lui b
(exclusiv b), iar b este egal cu suma divizorilor lui a (exclusiv a). De exemplu, a=220 și
6
b=284 sunt prietene. Scrieți un program care să determine primele trei perechi de numere
prietene, cu a<b. (5)
a=int(input("Introduceti a="))
b=int(input("Introduceti b="))
sd1=0.0
sd2=0.0
for d in range (1,a//2):
if (a%d==0):
sd1=sd1+d
for c in range (1,b//2):
if(b%c==0):
sd2=sd2+c
if a==sd2 and b==sd1:
print ("Numerele a,b sunt prietene.")
else:
print ("Numerele a,b NU sunt prietene.")
15. Se citește de la tastatură un număr natural nenul n, apoi se citesc succesiv n valori întregi.
Scrieți un program care să verifice dacă oricare număr dintre cele citite are un număr impar
de divizori primi. (5)
n=int(input("Introduceti numarul n="))
for i in range(0,n):
dp=0
for k in range(2,n):
if(i==2):
prim=0 #0=fals, 1=adevarat
else:
if(i%2==0):
prim=0
else:
prim=1
7
if(prim==1):
if (k % i == 0):
dp=dp+1
if (dp%2!=0):
gasit=1
if (gasit==1):
print("Da.")
else:
print("Nu.")
17. Să se demonstreze prin intermediul unui program că orice număr natural n>3 se poate scrie
ca sumă de două numere prime. Programul citește o valoarea maximă naturală Vmax>4 prin
intermediul căruia se va verifica dacă proprietatea este adevărată. (5)
vmax=int(input("vmax="))
for i in range (4,vmax):
s=0
s1=0
8
for j in range(2,i):
if(j%i==0):
s=s+1
if(s==0):
for k in range (4,vmax):
for j in range(2,k):
if(k%j==0):
s1=s1+1
if(s1==0):
if (k+j==vmax):
print("i=", i)
18. Scrieți un program care numără câte linii conține un fișier text.
import random
with open("date.txt", 'r') as fp:
x = len(fp.readlines())
print("Numarul total de linii este:", x)
S-a folosit fisierul intitulat “date.txt”, care contine urmatoarele date:
19. În fișierul numere.txt se află numere naturale din intervalul [0. 1000], separate prin spații. Să
se creeze fișierul impare.txt care să conțină doar valorile pare din fișierul numere.txt, câte o
valoare pe linie.
numerefisier = open('numere.txt', 'r')
imparefisier = open('impare.txt', 'w')
while True:
x=int(numerefisier.readline())
if (x % 2 == 0):
9
imparefisier.write(str(x)+'\n')
numerefisier.close()
imparefisier.close()
20. Scrieți un program care să testeze dacă două fișiere ale căror nume se citesc de la tastatură
sunt identice.
fisier1 = open('fisier1.txt', 'r')
fisier2 = open('fisier2.txt', 'r')
i=0
for linia1 in fisier1:
i=i+1
for linia2 in fisier2:
if linia1 == linia2:
print("Linia", i, "este identica in ambele fisiere.")
else:
print("linia", i, ":")
print("\tFisierul 1:", linia1, end='')
print("\tFisierul 2:", linia2, end='')
break
fisier1.close()
fisier2.close()
21.
fisier1=open('nrin.txt', 'r')
fisier2=open('nrout.txt', 'w')
n=int(input("Introduceti numarul n="))
d=2
with open('nrin.txt', 'r') as fisier1:
10
while n>1:
p=0
while n%d==0:
p +=1
n /= d
if p:
print (p)
d +=1
if n>1 and d * d > n:
d=n
fisier2.write(d)
fisier2.write(p)
fisier1.close()
fisier2.close()
def factori_primi(n):
i=2
d = []
p = []
while i * 1 <= n :
if n % i :
i += 1
else:
n //= i
d.append(i)
if n > 1 :
p.append(n)
return d,p
fwrite=open('nrout.txt', 'a')
with open ("nrint.txt", "r") as f:
for l in f:
for i in [int(a) for a l.split()]:
fwrite.write("p=")
fwrite.write(str(factori_primi(i[0][0: ])))
fwrite.write("\n")
fwrite.write("d=")
fwrite.write(str(factori_primi(i)[1][0: ]))
fwrite.write("\n")
22.
11
with open("Temp.in",'r') as temp:
nrp=0
nrs=0
i=100
for x in temp:
if int(x) > 0:
nrp=nrp+1
if i!=100:
if int(x)-1 <= 0 and int(x) > 0 or int(x)-1 >0 and int(x)<=0:
nrs=nrs+1
i=int(x)
print("Nr. zile cu temperaturi pozitive: ", nrp)
print("Nr. de schimbari de semn: ", nrs)
23.
12
n=int(input("lungimea vectorului este: "))
vintrodus=[]
vector_medii=0
if n<1 or n>50:
print("Nu ati introdus o valoare acceptata din intervalul agreat [1,50].")
else:
for i in range (1,n+1):
a=float(input("a["+str(i)+"]= "))
vintrodus.append(a)
print("Vectorul introdus este:", vintrodus)
with open ("vector_medii.txt",'w') as t:
for x in vintrodus:
print (vintrodus)
print (len(vintrodus))
print (sum(vintrodus))
average = (sum(vintrodus))/ (len(vintrodus))
13
t=average
print (t)
t.write(str(input(average))
24.
vector = [1, 2, 3, 4, 5]
n = len(vector)
rezultat = []
for i in range(n-1):
ma = (vector[i] + vector[i+1]) / 2
mg= (vector[i] * vector[i+1]) ** 0.5
mar = 2 / (1/vector[i] + 1/vector[i+1])
rezultat.append(ma)
rezultat.append(mg)
rezultat.append(mar)
print(rezultat)
25. Să se verifice dacă un șir de numere naturale nenule este un palindrom, prin metoda
verificării proprietății de palindrom cu o structură WHILE, cu care se parcurge vectorul până
la jumătate sau până când se găsește un element diferit de elementul situat la egală depărtare
de capătul celălalt al vectorului.
def verifica_palindrom(string):
lungime=len(string)
prima=0
ultima=lungime-1
status=lungime-1
while(prima<ultima):
if(string[prima]==string[ultima]):
prima=prima+1
ultima=ultima-1
else:
status=0
break
14
return int(status)
string=input("Introdu valoarea:")
status=verifica_palindrom(string)
if(status):
print("Este palindrom.")
else:
print("Nu este palindrom.")
26. Să se calculeze valoarea unui polinom într-un punct dat. Coeficienții polinomului sunt
numere reale, se introduc de la tastatură și se rețin într-un vector. Dacă gradul polinomului
este n, vectorul de coeficienți va conține n+1 elemente.
coeficienti=input("Introduceti coeficientii polinomului:")
x=str(input("Introduceti punctul in care se calculeaza valoarea polinomului:"))
coeficienti=str(x)
for x in coeficienti:
rezultat=coeficienti[0]
for i in range (1, len(coeficienti)):
rezultat=rezultat*x+coeficienti[i]
print("Valoarea polinomului in punctul dat este:", rezultat)
27. Pentru cei n studenţi ai anului întâi se cunosc notele ni (i=1,n) la primul examen. Se cere să
se determine numărul studenţilor cu nota 10, 9 şi 8 şi să se afişeze studenţii nepromovaţi.
n=int(input("Numarul studentilor care au sustinut examenul este de:"))
nr_studenti=[]
i=0
while (i!=n):
x=int(input("Nota obtinuta la examen:"))
nr_studenti.append(x)
i+=1
j=0
k=0 #numarul studentilor restantieri
q=0 #numarul studentilor cu nota 10
r=0 #numarul studentilor cu nota 9
t=0 #numarul studentilor cu nota 8
while (j!=n):
if (nr_studenti[j]<5):
k+=1
print("Numarul studentilor restantieri este de:", k)
elif (nr_studenti[j]>5):
if (nr_studenti[j]==10):
q+=1
print("Numarul studentilor cu nota 8 este de:",q)
elif (nr_studenti[j]==9):
r+=1
15
print("Numarul studentilor cu nota 9 este de:",r)
if (nr_studenti[j]==10):
k+=1
print("Numarul studentilor cu nota 10 este de:", q)
j+=1
28. Să se scrie programul care verifică dacă elementele unui vector pot forma o progresie
geometrică.
n=int(input("Numarul de elemente din vector:"))
vector=[]
i=0
while (i!=n):
x=float(input("Elementul a[" + str(i+1) + "] care apartine vectorului este:"))
vector.append(x)
i+=1
k=1
j=0
while (j!=n-2):
if (vector[j]*vector[j]==vector[j-1]*vector[j+1]):
k=0
j=j+1
if(k==0):
print("Elementele din vector se afla in progresie geometrica.")
else:
print("Elementele din vector NU se afla in progresie geometrica.")
29. Să se scrie programul care realizează determinarea elementului maxim de pe fiecare linie și a
elementului maxim dintr-o matrice dreptunghiulară de dimensiuni mxn.
m=int(input("Numar de linii:"))
n=int(input("Numar de coloane:"))
A=[]#matricea introdusa
for i in range(m):
for j in range(n):
a=int(input("a["+str(i+1)+str(j+1)+"]="))
A.append(a)
print("Matricea pe care ati introdus-o sub forma unei liste este:", A)
for i in range(m):
inceput=i*n
sfarsit=inceput+n
max_line=max(A[inceput:sfarsit])
print("Elementul maxim din linia", i+1, "este:", max_line)
max=0
for x in A:
if x>max:
16
max=x
print("Elementul maxim din aceasta matrice este:", max)
30. Să se scrie programul care realizează determinarea poziției primei apariții a unei valori date
într-o matrice dreptunghiulară, de dimensiuni mxn.
m=int(input("Numarul de linii: "))
n=int(input("Numarul de coloane: "))
A=[]
for i in range(m):
for j in range (n):
a=int(input("a["+str(i+1)+str(j+1)+"]= "))
A.append(a)
print("Matricea sub formă de lista:", A)
numar=int(input("Valoarea cautata este: "))
if A.__contains__(numar):
print("Pozitia primei aparitii a numarului", numar, "este", A.index(numar))
17
for i in range(n):
for j in range(i+1,n):
if matrice[i][n-i-1] < matrice[j][n-j-1]:
matrice[i][n-i-1], matrice[j][n-j-1] = matrice[j][n-j-1], matrice[i][n-i-1]
return matrice
33. Să se scrie un program care să determine toate perechile de numere naturale a, b, cu a≤b,
având proprietatea că nu au nici o cifră în comun și suma lor este egală cu S. Valoarea S se
citește de la tastatură. Fiecare pereche se va scrie pe un rând într-un fișier, cu un spațiu
între elementele ce compun perechea.
a. Prima metoda:
def functie_perechi(S):
with open("perechi.txt", 'w') as f:
for a in range(S):
for b in range(a, S):
cc=False #cc=cifra comuna pe care o va detecta programul
for ca in str(a): #ca=cifra a
for cb in str(b): #cb=cifra b
if ca==cb:
cc=True
if cc:
break
if cc==False and S==a+b:
f.write=("Perechile gasite sunt:", f"{a}{b}\n")
#Se introduce suma de la tastatura
S=int(input("Introduceti valoarea sumei:"))
functie_perechi(S)
b. Metoda 2:
import itertools
def remove_adjacent_duplicates(string):
result = []
for char in string:
if len(result) == 0 or char != result[-1]:
result.append(char)
return "".join(result)
36. Să se elaboreze programul pentru crearea unui vector cu elementele pozitive dintr-un vector
dat.
vector = []
n = int(input("Numarul elementelor din vector: "))
19
for i in range(0, n, 1):
x = int(input("Numar: "))
vector.append(x)
for j in range(0, n, 1):
if(vector[j]>0):
vector_nr_pozitive=[vector[j]]
print (vector_nr_pozitive)
37. Să se scrie un program care citește valoarea lui x, pozitiv, afișează valorile lui x, [x], {x}, iar
apoi calculează și afișează valoarea următoarei expresii : 7*[x]*x+6*{x}-9.
import math
x=float(input("Introduceti x: "))
parti = math.modf(x)
#functia modf() extrage partea fractionara si partea intreaga si le retine sub forma de tuplu
#parti[0] este partea fractionara si parti[1] este partea intreaga
pf = float(format(parti[0], '.3f'))
pi = int(parti[1])
print("x= \t", x)
print("[x]= \t", pi)
print("{x}= \t", pf)
print("e=", 7*x*pi-6*pf-9)
38. Să se elaboreze programul pentru crearea unui vector cu elementele pare dintr-un vector dat.
vector = []
n = int(input("Numarul elementelor din vector: "))
for i in range(0, n, 1):
x = int(input("Numar: "))
vector.append(x)
for j in range(0, n, 1):
if vector[j]%2==0:
vector_nr_pare=[vector[j]]
print(vector_nr_pare)
39. Să se scrie un program care citește valoarea lui x, pozitiv, afișează valorile lui x, [x], {x}, iar
apoi calculează și afișează valoarea următoarei expresii: 5*[x]*{x}-8*x+10.
import math
x=float(input("Introduceti x: "))
parti = math.modf(x)
#functia modf() extrage partea fractionara si partea intreaga si le retine sub forma de tuplu
#parti[0] este partea fractionara si parti[1] este partea intreaga
pf = float(format(parti[0], '.3f'))
pi = int(parti[1])
print("x= \t", x)
print("[x]= \t", pi)
print("{x}= \t", pf)
print("e=", 9*pi*pf-8*x+10)
20
40. Să se elaboreze programul pentru numărarea aparițiilor unei valori date într-un vector.
vector = []
n = int(input("Numarul elementelor din vector: "))
for i in range(0, n, 1):
x = int(input("Numar: "))
vector.append(x)
count=int(0)
for j in range(0, n, 1):
if(vector[j]==x):
count=count+1
print (count)
41. Să se scrie un program care citește valoarea lui x, pozitiv, afișează valorile lui x, [x], {x}, iar
apoi calculează și afișează valoarea următoarei expresii: 5*[x]*{x}-8*x+10.
import math
x=float(input("Introduceti x: "))
parti = math.modf(x)
#functia modf() extrage partea fractionara si partea intreaga si le retine sub forma de tuplu
#parti[0] este partea fractionara si parti[1] este partea intreaga
pf = float(format(parti[0], '.3f'))
pi = int(parti[1])
print("x= \t", x)
print("[x]= \t", pi)
print("{x}= \t", pf)
print("e=", 9*pi*pf-8*x+10)
42. Să se elaboreze programul pentru numărarea aparițiilor unei valori date într-un vector.
vector = []
n = int(input("Numarul elementelor din vector: "))
for i in range(0, n, 1):
x = int(input("Numar: "))
vector.append(x)
count=int(0)
for j in range(0, n, 1):
if(vector[j]==x):
count=count+1
print (count)
43. Să se scrie un program care citește valoarea lui x, pozitiv, afișează valorile lui x, [x], {x}, iar
apoi calculează și afișează valoarea următoarei expresii : 5*{x}*x+3*[x]-4.
21
import math
x=float(input("Introduceti x: "))
parti = math.modf(x)
pf = float(format(parti[0], '.3f'))
pi = int(parti[1])
print("x= \t", x)
print("[x]= \t", pi)
print("{x}= \t", pf)
print("e=",5*pf*x+3*pi-4)
44. Să se elaboreze programul pentru numărarea elementelor dintr-un vector mai mari decât o
valoare dată.
vector = []
n = int(input("Numarul elementelor din vector: "))
for i in range(0, n, 1):
x = int(input("Numar: "))
vector.append(x)
count=0
k = int(input("Dati valoarea pe care doriti sa o verificati:"))
for j in range(0, n, 1):
if(vector[j]>k):
count=count+1
print (count)
22
PC 1 – CURSUL 7 SILVESTRU CĂTĂLIN
Cursul 7
FUNCȚII ȘI PARAMETRII
Rezolvarea unor aplicații complexe se realizează prin descompunerea problemei în
subprobleme relativ independente; fiecare dintre aceste subprobleme conținând module de program mai
simple. O problemă rezolvată doar în cadrul funcției main() este ineficientă și poate prezenta multe
probleme. Asemenea unei echipe, fiecare modul de program are rolul său predefinit, împreună
rezolvând problema rapid și eficient. Pentru ca programul să funcționeze, este necesar ca modulele de
program să fie asamblate. Prin urmare, într-o etapă preliminară implementării se face o analiză a
problemei de rezolvat, se stabilesc subproblemele și modulele de program care trebuie să rezolve aceste
subprobleme, precum și modalitățile în care acestea trebuie să comunice între ele (interfața).
Frecvent apar probleme cu operații repetitive. Pentru astfel de situații, cea mai bună soluție este
de a scrie secvența de program corespunzătoare operației o singură dată iar apoi să fie folosită ori de
câte ori este nevoie de ea.
Realizarea unui program de complexitate mare necesită organizarea datelor și prelucrarea la
care acestea trebuie supuse în subprobleme și, corespunzător acestora, sub formă de subprograme.
În limbajele C și C++, subprogramele sunt denumite funcții. Acestea reprezintă un element
fundamental al limbajului C/C++, orice program fiind constituit dintr-o succesiune de funcții, una
singură fiind cea principala – main(). La lansarea în execuție a unui program este apelată funcția
main().
În concluzie, toate prelucrările dintr-un program C/C++ sunt organizate ca o ierarhie de apeluri
de funcții, baza acesteia fiind funcția main().
Schematic, un program ce conține mai multe funcții se prezintă astfel:
Programul începe din main(), de unde mai apoi sunt apelate funcțiile subprogram, unele la
rândul lor având alte subprograme.
Pentru a putea dezvolta și utiliza funții proprii este necesară cunoașterea definirii, declarării și
apelării acestora.
1
PC 1 – CURSUL 7 SILVESTRU CĂTĂLIN
Definirea unei funcții este constituită dintr-un antet, ce conține: numele funcției, tipul
rezultatului returnat de funcție, lista parametrilor funcției și un bloc de instrucțiuni care descrie
prelucrările efectuate de funcție.
Formatul general al unei funcții este:
tip nume(lista_parametrii_formali)
{
declaratii_variabile_locale;
instructiuni;
}
tip – reprezintă tipul rezultatului returnat de funcție. Dacă tipul nu este specificat, se
consideră implicit int. Dacă funcția nu trebuie să returneze nici o valoare, se specifică
drept tip al rezultatului void.
nume – reprezintă numele funcției.
lista_parametrii_formali – reprezintă una sau mai multe declarații de parametrii
formali, separate prin virgulă, ori poate lipsi (parantezele rotunde apar în continuare).
În cazul în care lista de parametrii formali este vidă, trebuie să se specifice explicit
acest lucru prin cuvântul rezervat void.
2
PC 1 – CURSUL 7 SILVESTRU CĂTĂLIN
Exemplul 2
void suma(int a, int b)
{ printf(“a+b=%d\n”, a+b); }
/* o funcție denumită suma ce primește ca parametrii două numere întregi și
afișează suma acestora. Funcția nu returnează nici un rezultat. */
Exemplul 3
int suma_vector(int v[100], int n)
{
int i, s=0;
for(i=0; i<n; s+=v[i++]);
return s;
}
/* o funcție denumită suma_vector ce primește ca parametrii un vector cu
maxim 100 de elemente întregi și numărul de elemente din vector. Funcția
returnează sume elementelor vectorului. */
Exemplul 4
int min(int x, int y)
{
if(x<y)
return x;
return y;
}
/* într-o funcție pot fi prezente mai multe instrucțiuni return, una singură
fiind executată! În funcția min sunt declarați doi parametrii întregi, x și
y. Dacă x<y, atunci funcția returnează valoarea parametrului x iar execuția
funcției se încheie. Dacă nu, este executată următoarea instrucțiune de după
3
PC 1 – CURSUL 7 SILVESTRU CĂTĂLIN
O declarație de funcție este alcătuită din antetul funcției, urmat de caracterul ‘;’, nu de blocul
de instrucțiuni al funcției. Spre deosebire de definiție, lista parametrilor din declarație nu trebuie să
conțină în mod obligatoriu și numele parametrilor, ci este permisă doar specificarea tipurilor
parametrilor.
Un program C/C++ poate conține pentru o funcție o singură definiție și oricâte declarații.
Atunci când este întâlnit un apel de funcție, compilatorul trebuie să verifice validitatea acestuia. Este
necesar astfel ca înaintea oricărui apel de funcție să apară fie definiția funcției, fie declarația acesteia.
Exemplul 2
void f2(int, unsigned);
/* funcția f2() conține doi parametrii (unul de tip int și unul de tip
unsigned) și nu returnează nici un rezultat. În cadrul acestei declarații
este specificat doar tipul parametrilor. */
Exemplul 3
double sqrt(double);
/* funcția sqrt() conține un parametru de tip double și returnează radicalul
valorii primite ca parametru. Această funcție este declarată în fișierul
antet math.h. */
4
PC 1 – CURSUL 7 SILVESTRU CĂTĂLIN
Atenție! Parametrii actuali trebuie să corespundă cu parametrii formali ca număr, ordine și tip.
În cazul în care se dorește utilizarea valorii returnate de funcție ca operand într-o expresie, se
va apela funcția în formatul general, însă fără caracterul ‘;’.
Mai apare întrebarea: unde sunt memorate valorile parametrilor actuali?
Funcțiile utilizează o zonă de memorie de tip stivă. La apelarea funcției, se alocă spațiu de
memorie pe stivă pentru valorile parametrilor actuali (transfer prin valoare). La încheierea execuției
funcției, zona de memorie alocată pe stiva pentru apel este eliberată. Prin urmare, chiar dacă în blocul
funcției sunt modificate valorile parametrilor, la ieșirea din funcție se pierd valorile modificate,
deoarece memoria alocată pentru parametrii se eliberează.
Dacă se dorește ca la ieșirea din funcție să fie păstrate valorile modificate ale parametrilor,
atunci nu se transmit ca parametrii expresii ale căror valori să fie copiate pe stivă, ci se transmit adresele
de memorie ale variabilelor ale căror valori se doresc a fi modificate.
5
PC 1 – CURSUL 7 SILVESTRU CĂTĂLIN
Exemplul 2
int a=6, b=4;
printf(“valoarea minima este: %d”, min(a, b));
/* acest exemplu este același cu cel anterior, diferența fiind faptul că nu
mai este folosită o variabilă m în care este returnată rezultatul funcției
min, apelarea fiind realizată ca operand într-o expresie de afișare. Prin
urmare, rezultatul este returnat și afișat direct în continuarea unui mesaj
descriptive. */
Exemplul 3
void schimb(int x, int y)
{
int aux;
aux=x; x=y; y=aux;
printf(“x=%d\ty=%d\n”, x, y);
}
/* o funcție ce realizează interschimbarea valorilor parametrilor x și y */
int a=4, b=7; //a și b au fost declarate
schimb(a, b);
printf(“a=%d\tb=%d\n”, a, b);
/* în urma execuției celor 2 instrucțiuni (apelarea funcției respective
afișarea celor 2 variabile) se va afișa:
x=7 y=4
a=4 b=7
Prin urmare, deși valorile parametrilor x și y au fost schimbate în blocul
funcției, la ieșirea din funcție valorile variabilelor a și b care au fost
utilizate ca parametri actuali rămân neschimbate. Funcția nu poate modifica
valorile parametrilor actuali, deoarece nu cunoaște adresa la care aceștia
sunt memorați. */
6
PC 1 – CURSUL 7 SILVESTRU CĂTĂLIN
Se observă de data aceasta că parametrii funcției nu sunt numere întregi, ci pointeri la întregi.
La apelul funcției se ține cont de tipul parametrilor, astfel încât, se vor transmite adresele variabilelor
ale căror valori trebuie să fie interschimbate, print includerea operatorului de referință (&).
Valoarea atribuită unei referințe nu poate fi schimbată, aceasta fiind valabilă pe toată durata
execuției blocului de program în care este declarată.
Chiar dacă referința seamănă cu o variabilă, aceasta nu este însăși variabilă, ci doar o referire
la variabila respectivă (o adresă).
7
PC 1 – CURSUL 7 SILVESTRU CĂTĂLIN
Referințele nu sunt cele mai utile. Funcția schimb() ce intenționa interschimbarea valorilor
parametrilor x și y, folosind de data aceasta referințele către parametrii care trebuie modificați va arăta
astfel:
void schimb(int &x, int &y)
{
int aux;
aux=x; x=y; y=aux;
cout<<”x=”<<x<<” y=”<<y<<endl;
}
//se vor presupune aceleași valori + cele două referințe ale variabilelor a
și b:
int a=4; b=7;
int &ra=a; &rb=b;
//secvența de instrucțiuni:
schimb(ra, rb);
cout<<”a=”<<a<<” b=”<<b<<endl;
/* se va afișa pe ecran:
x=7 y=4
a=7 b=4
interschimbarea fiind realizată cu succes */
8
PC 1 – CURSUL 7 SILVESTRU CĂTĂLIN
Varianta C
#include <stdio.h>
9
PC 1 – CURSUL 7 SILVESTRU CĂTĂLIN
În acest exemplu au fost declarate cele două funcții de citire, respectiv afișare a vectorilor.
Funcția de cit_vector() are doi parametri pointeri de tip int. Primul parametru (vectorul)
este declarat ca pointer și îi este specificat și numărul maxim de elemente componente din vector. Al
doilea parametru (numărul de elemente din vector) este de asemenea declarat ca pointer, deoarece
funcția trebuie să transmită în exterior valoarea citită.
La apelul funcțiilor se observă că este precizat doar numele vectorului care se citește, respectiv
se afișează. Numele vectorului fiind el însăși un pointer către primul element din vector, determină ca
apelul funcției de afișare să fie corect și fără includerea operatorului de referință ‘&’.
Varianta C++
#include <iostream.h>
int main()
{
int n, m, v[10], p[10];
cit_vector(v, n);
cit_vector(p, m);
afi_vector(v, n);
afi_vector(p, m);
}
10
PC 1 – CURSUL 7 SILVESTRU CĂTĂLIN
7.6. Proiecte
O aplicație C/C++ nu este constituită doar dintr-un singur fișier sursă. Există mai multe fișiere
ce constituie programul și în cadrul cărora se găsesc definițiile funcțiilor din program. Faptul că un
program este alcătuit din mai multe fișiere permite reutilizarea anumitor funcții într-o altă aplicație
(funcții ce au fost deja elaborate într-un fișier sursă ce pot fi utilizate în altă aplicație), precum și
posibilitatea elaborării aplicațiilor în echipă (mai mulți membrii pot crea propriile fișiere sursă ce pot fi
apoi puse cap la cap spre crearea programului final).
În final, fișierele sursă elaborate sunt asamblate pentru a da naștere unui proiect.
Exemplu de proiect
Un proiect simplu format din două fișiere sursă:
fișierul radical.cpp – reprezintă fișierul sursă ce conține definițiile a două funcții proprii
de determinare a radicalului:
R2() – pentru radicalul de ordin 2;
R3() – pentru radicalul de ordin 3.
11
PC 1 – CURSUL 7 SILVESTRU CĂTĂLIN
Pentru calculul valorii radicalului de ordin doi se va folosi algoritmul lui Hero, metodă ce
folosește pentru extragerea radicalului de ordin doi din numărul real pozitiv a formula de recurență:
1 𝑎
𝑥𝑛 = (𝑥𝑛−1 + ) , 𝑛 = 1,2 …
2 𝑥𝑛−1
Pentru radicalul de ordin trei se folosește o formulă de recurență similară:
1 𝑎
𝑥𝑛 = (2𝑥𝑛−1 + 2 ) , 𝑛 = 1,2 …
3 𝑥𝑛−1
Din punct de vedere matematic, după efectuarea unui anumit număr de pași, valoarea calculată
a lui 𝑥𝑛 oferă o valoare aproximativă pentru √𝑎.
Pentru implementarea relațiilor de recurență se folosesc două variabile:
- x0 ce reține termenul precedent;
- x1 ce preia valoarea calculului termenului curent.
Se va considera că după șapte pași se obține o aproximație suficient de bună pentru calculul
valorilor respective cu trei zecimale exact. Numărul de pași poate fi determinat exact prin condiția de
oprire |𝑥1 − 𝑥0| < 10−3, ceea ce exprimă matematic faptul că primele trei zecimale ale celor două
aproximații succesive sunt identice.
Pentru realizarea aplicației pe baza unui exemplu, se cere determinarea radicalului de ordin 2,
respectiv ordin 3, a numărului natural 10. În continuare sunt prezentate două tabele; în primul tabel este
prezentat cazul calculării radicalului de ordin 2, iar în al doilea, cazul calculării radicalului de ordin 3.
Pas x0 x1 Pas x0 x1
1 10.000 5.500 1 10.000 6.700
2 5.500 3.659 2 6.700 4.541
3 3.659 3.196 3 4.541 3.189
4 3.196 3.162 4 3.189 2.454
5 3.162 3.162 5 2.454 2.189
6 2.189 2.155
7 2.155 2.154
8 2.154 2.154
Se observă că, în cazul calculării radicalului de ordin 2 s-a ajuns la trei zecimale exacte după
patru pași, iar în cazul calculării radicalului de ordin 3 s-a ajuns la trei zecimale exacte după șapte pași.
12
PC 1 – CURSUL 7 SILVESTRU CĂTĂLIN
double R3(double a)
{
double x0=a, x1;
for(int i=1; i<=7; i++)
{
x1=(1/3.0)*(2*x0+a/(x0*x0));
x0=x1;
}
return x1;
}
fișierul F.cpp – reprezintă fișierul sursă, alcătuit doar din funcția main(), în cadrul căreia
vor fi apelate cele două funcții:
#include <stdio.h>
#include “radical.h”
void main()
{
double a;
printf(“a= “); scanf(“%lf”, &a);
printf(“Radical de ordin 2 = %.3lf\n”, R2(a));
printf(“Radical de ordin 3 = %.3lf\n”, R3(a));
}
13
PC 1 – CURSUL 7 SILVESTRU CĂTĂLIN
Fișierul F.cpp include header-ul radical.h, care conține prototipurile celor două funcții de
determinare a radicalului:
double R2(double);
double R3(double);
14
PC 1 – CURSUL 6 SILVESTRU CĂTĂLIN
Cursul 6
POINTERI, STRUCTURI, STIVE ȘI COZI
6.1. Pointeri
Pointerii sunt definiți drept date ce au ca valori adrese de memorie.
Pointerii sunt utilizați cu precădere în următoarele cazuri: prelucrarea tablourilor, transferul
parametrilor funcțiilor, accesul direct la memorie și alocarea dinamică a memoriei.
Unde tip reprezintă tipul de bază al pointerului, ce indică tipul datelor memorate la adresa
conținută în variabila_pointer.
1
PC 1 – CURSUL 6 SILVESTRU CĂTĂLIN
2. Operația de dereferențiere
Operația inversă referențierii – accesarea valorii memorate într-o zonă de memorie a cărei
adresă de început este memorată într-un pointer. Dereferențierea unui pointer se realizează cu ajutorul
operatorului unar * (numit operator de dereferențiere):
*variabila_pointer;
Exemplu:
int i=8, *p;
p=&i; /*referențiere – atribuirea pointerului p adresa variabilei i*/
cout<<*p; /*dereferențiere – afișarea conținutului zonei de memorie a
cărei adresă este memorată în p*/
3. Incrementare/decrementare
Operatorii de incrementare ‘++’ respectiv de decrementare ‘--‘ pot fi aplicați asupra unui
pointer. Efectul obținut este mărirea/micșorarea adresei memorate în pointer cu numărul de octeți
necesari pentru a memora o dată de tipul de bază al pointerului (sizeof(tip)).
Exemplu:
long int * p; p++; //adresa memorată în p se mărește cu 4
char * p; p--; //adresa memorată în p se micșorează cu 1
2
PC 1 – CURSUL 6 SILVESTRU CĂTĂLIN
6. Comparații
Pointeri-lor cu același tip de bază pot fi aplicați operatorii relaționali și de egalitate, cu
semnificația uzuală.
Exemplu:
if(p==NULL) ...
O matrice este un vector de vectori. Prin urmare, numele său este un pointer la prima linie din
matrice:
tip v[LgMax][LgMax];
Se consideră spre exemplu un șir s de maxim 100 de caractere:
char s[100];
3
PC 1 – CURSUL 6 SILVESTRU CĂTĂLIN
Se cere determinarea lungimii efective a șirului de caractere. Acest lucru poate fi realizat prin
parcurgerea șirului până la întâlnirea marcajului de sfârșit de șir și numărând caracterele parcurse.
int lungime;
for(lungime=0; s[lungime]; lungime++);
Același lucru se poate realiza și cu ajutorul unui pointer ce inițial va avea ca valoare adresa de
început a șirului. Pointerul va fi incrementat cât timp valoarea lui nu este adresa zonei de memorie care
conține caracterul NULL. Lungimea șirului va fi astfel determinată prin diferența valorii pointerului
care indică la sfârșit adresa caracterului NULL și valoarea pointerului care indică adresa primului
caracter din șir:
char *p;
for(p=s; *p; p++);
lungime=p-s;
4
PC 1 – CURSUL 6 SILVESTRU CĂTĂLIN
Prin utilizarea funcției strcmp(), la comparare se face distincție între caracterele de tip litere
mari și caracterele de tip litere mici. Pentru a nu se ține cont de acest lucru la comparare, se utilizează
funcția stricmp(), ce are același format ca și strcmp().
5
PC 1 – CURSUL 6 SILVESTRU CĂTĂLIN
unde separatori reprezintă șirul de caractere de tip separatori memorați în memorie. Primul apel al
funcției va returna adresa de început a primei unități lexicale din șir și va înlocui în șir acea primă unitate
lexicală cu un caracter NULL. Dacă însă șirul este vin ori nu conține separatori, funcția va returna
valoarea NULL.
A doua apelare a funcției va avea rolul de extragere succesivă a celorlalte unități lexicale din
șir; de această dată însă, primul parametru trebuie să fie NULL (în caz contrar, funcția va returna în
continuare prima unitate lexicală din șir):
strtok(NULL, separatori);
La apelarea sub această formă, funcția va returna adresa de început a următoare unități lexicale
din șir, ori va returna NULL în cazul în care nu mai există unități lexicale. După fiecare apel, primul
separator de după unitate lexicală identificată este înlocuit de caracterul NULL.
Prototipul funcției este:
char * strtok(char *sir, const char *separatori);
6
PC 1 – CURSUL 6 SILVESTRU CĂTĂLIN
Funcții de conversie
În header-ul stdlib.h sunt declarate funcții ce permit conversia unui număr întreg într-un șir de
caractere:
char *itoa(int nr, char *sir, int baza);
//converteste un numar de tip int
char *ltoa(long nr, char *sir, int baza);
//converteste un numar de tip long
char *ultoa(unsigned long nr, char *sir, int baza);
//converteste un numar de tip unsigned long
7
PC 1 – CURSUL 6 SILVESTRU CĂTĂLIN
Lista de variabile este opțională, nefiind obligatorie definirea variabilelor de tip respectiv atunci
când este definit tipul struct.Definirea variabilelor poate fi realizată și ulterior, sub forma:
struct nume_tip lista_variabile;
De asemenea, numele tipului structurii este opțional, fiind posibilă declararea unor structuri
anonime (este posibilă o singură declarare de acest gen). În acest caz însă, definirea listei de variabile
devine obligatorie.
O declarație de câmpuri are sintaxa unei declarații de variabile uzuale:
tip nume_camp_1, nume_camp_2, ..., nume_camp_n;
Articolul punct conține cele două câmpuri reale x și y ce au rolul de abscisă, respectiv ordonată
a punctului. Nu au fost declarate variabile de tip punct, însă, acestea pot fi declarate ulterior astfel:
struct punct P1, P2; //două puncte de coordonate x și y
2. O structură denumită Elev ce conține informații precum: nume, mediile la 12 discipline, media
generală, adresa și numărul de telefon (pe scurt, o bază de date a elevilor):
struct elev
{char nume[30], adresa[60];
float medii[12], MG;
unsignd long tel;} clasa[30];
8
PC 1 – CURSUL 6 SILVESTRU CĂTĂLIN
9
PC 1 – CURSUL 6 SILVESTRU CĂTĂLIN
typedef struct
{char nume[30];
int pret;}; Produs
/* asocierea numelui Produs unui tip articol care contine numele si
pretul produsului */
6.4. Stiva
O stivă este o structură de date abstractă pentru care atât operația de inserare a unui element în
structură, cât și operația de extragere a unui element, se realizează la un singur capăt (numit vârful
stivei).
În timpul prelucrării stivelor, singurul element dintr-o stivă la care
se are acces este vârful stivei.
O stivă poate fi asemănată cel mai bine cu un teanc de farfurii.
Atunci când se dorește plasarea unei farfurii în teanc, aceasta va fi plasată
deasupra; în cazul preluării, se ia o farfurie tot de deasupra. Adăugarea ori
preluarea unei farfurii oriunde din teanc duce la spargerea farfuriilor
(variantă nedorită).
Prin urmare, singurele operații ce pot fi executate cu o stivă sunt:
crearea unei stive vide;
inserarea unui element în stivă (operație cunoscută ca PUSH);
extragerea unui element din stivă (operație cunoscută ca POP);
accesarea elementului de la vârf (operație cunoscută ca TOP).
În concluzie, ultimul element înserat în stivă este primul extras. De aceea, o stivă poate fi văzută
ca o structură de date (mai exact un vector) a cărei funcționare merge după principiul „ultimul intrat
primul ieșit” (LIFO – Last In First Out).
6.4.1. Utilizare
Stiva joacă un rol important în programare, de pildă, în cazuri în care este necesară memorarea
unor informații și regăsirea acestora într-o anumită ordine, descrisă de principiul LIFO; ori atunci când
programul trebuie să amâne execuția unor operații, pentru a fi executate ulterior în ordinea inversă
apariției lor.
10
PC 1 – CURSUL 6 SILVESTRU CĂTĂLIN
Un dezavantaj al implementării stivelor ca vectori alocați static constă în faptul că, indiferent
de numărul de elemente existente în stivă, dimensiunea zonei de memorie alocată stivei este aceeași.
6.4.2. Implementare
Stiva poate fi implementată în diverse moduri, de pildă, ca un vector în care sunt reținute
elementele stivei. Pentru ca vectorul să se comporte asemenea unei stive, singurele operații permise
asupra acestuia sunt operațiile caracteristice stivei.
În exemplul următor este declarată o stivă cu elemente de tip int:
11
PC 1 – CURSUL 6 SILVESTRU CĂTĂLIN
Accesarea elementelor
Datorită modului său restrictiv de funcționare, stiva permite numai accesarea elementului din
vârf. Dacă se dorește aflarea altui element din stivă, atunci este necesară extragerea din stivă a tuturor
celorlalte elemente, până când se ajunge la elementul dorit.
Accesarea elementului din vârf presupune determinarea valorii acestuia, valoare ce poate fi
reținută într-o variabilă:
x=S[vf];
6.5. Coada
Coada este o structură de date abstractă pentru care operația de inserare a unui element se
realizează la un capăt, în timp ce operația de extragere a unui element se realizează la celălalt capăt.
Singurul element dintr-o coadă la care este posibil accesul direct este cel de la început.
O coadă este ușor de exemplificat – de pildă, o linie de așteptare la un ghișeu. Orice situație în
care apar mai multe cereri de acces la o resursă unică necesită formarea unei „linii de așteptare”. Dacă
nu apar priorități, cererile sunt satisfăcute în ordinea sosirii.
Datorită faptului că întotdeauna este extras primul element din coadă, iar inserarea oricărui nou
element se face la sfârșit, coada este definită ca o structură de date care funcționează după principiul
„primul intrat primul ieșir” (FIFO – First In First Out).
6.5.1. Utilizare
O structură de date de tip coadă este necesară atunci când este necesar ca informațiile să fie
prelucrate exact în ordinea „sosirii” acestora, fiind reținute în coadă până când pot fi prelucrate. Cel mai
întâlnit exemplu de coadă este atunci când o rețea de calculatoare este conectată la o singură imprimantă.
Atunci când mai mulți utilizatori ai rețelei dau comenzi de tipărire, imprimanta va răspunde tuturor
acelor comenzi în urma înregistrării lor în coadă (Print Queue).
6.5.2. Implementare
Coada poate fi implementată în diferite moduri, de pildă, asemenea unei stive, coada poate fi fi
implementată static, reținând elementele sale într-un vector.
În exemplul următor este declarată o coadă cu elemente de tip int alocată static:
#define dim_max 50 //lungimea maximă a cozii
12
PC 1 – CURSUL 6 SILVESTRU CĂTĂLIN
Accesarea elementelor
Datorită modului său restrictiv de funcționare, coada permite numai accesarea primului
element. Dacă se dorește aflarea altui element din coadă, atunci este necesară extragerea din coadă a
tuturor celorlalte elemente, până când se ajunge la elementul dorit.
Accesarea primului element presupune determinarea valorii acestuia, valoare ce poate fi
reținută într-o variabilă:
x=C[inc];
13
PC 1 – CURSUL 5 SILVESTRU CĂTĂLIN
Cursul 5
TABLOURI DE DATE ȘI ȘIRURI DE
CARACTERE
O structură de date reprezintă o colecție/ansamblu de date, organizate după anumite reguli
dependente de tipul de structură. Aceste structuri sunt operative atunci când un program trebuie să
prelucreze un volum mare de date în mod eficient și organizat.
Astfel este declarat un tablou format dintr-un număr finit de elemente (nr_elem trebuie să fie
obligatoriu o expresie constantă) de un anumit tip. Vizual, un tablou de acest gen arată ca un șir de
„căsuțe” aliniate:
Putem să ne referim la un tablou cu ajutorul numelui său, iar pentru a ne referi la un element
din cadrul acestuia este necesar a fi specificat numele tabloului și poziția elementului în cadrul tabloului
prin numărul său de ordine (numerotarea începe implicit de la 0).
nume[indice];
Elementul indice al tabloului reprezintă numărul de ordine al elementului din tablou, cuprins
între 0 și nr_elem-1, cuprins între parantezele drepte ce constituie operatorul de indexare. Acest operator
de indexare are prioritate maximă; chiar mai mare decât operatorii unari!
Dimensiunea zonei de memorie necesară unui tablou se calculează înmulțind numărul de
elemente cu numărul de octeți necesari pentru memorarea unui element (nr_elem*sizeof(tip)).
Compilatorul verifică la întâlnirea declarației de variabilă a tabloului dacă dimensiunea zonei de
memorie necesară pentru memorarea tabloului nu depășite memoria disponibilă.
Exemple de declarare a unui tablou:
int t1[10]; /* un tablou cu 10 elemente de tip int, cu elementele t1[0],
t1[1], …, t1[9] */
float t2[100]; //un tablou cu 100 de elemente de tip float
1
PC 1 – CURSUL 5 SILVESTRU CĂTĂLIN
Astfel, se vor atribui în ordinea elementelor tabloului valorile din lista de inițializare. Dacă
tabloul este integral inițializat la declarație, nu mai este necesară specificarea dimensiunii sale, fiind
considerată egală cu numărul de valori din lista. Spre exemplu:
int t[]={10, 15, 20, 4}; //un tablou cu 4 elemente de tip int
Un astfel de tablou, în care la declarare este specificată o singură dimensiune, iar poziția unui
element este specificată utilizând un singur indice, se numește tablou unidimensional sau vector!
Elementele unui tablou pot fi de orice tip al limbajului, inclusiv de tip tablou, din care rezultă
un tablou bidimensional sau matrice. La un astfel de tablou trebuie la declarare specificate două
dimensiuni, poziția unui element fiind specificată utilizând doi indici:
tip nume[nre1][nre2];
Putem vedea un astfel de tablou ca o tablă de șah, în care primul element reprezintă numărul de
linii iar al doilea element reprezintă numărul de coloane. Spre exemplu, declararea unui tablou cu 2 linii
și 3 coloane inițializat cu valori:
int m[2][3]={{1,2,3},{4,5,6}}; /* o matrice cu 6 elemente de tip int
distribuite pe 2 linii și 3 coloane */
Pentru a putea face referire la un element din matrice este necesară specificarea numelui
matricei, indicele de linie și indicele de coloană astfel:
nume[ind_linie][ind_col];
De asemenea, se poate merge mai departe! Elementele unui tablou bidimensional pot fi de tip
tablou, obținând astfel tablouri multidimensionale. Spre exemplu, un tablou tridimensional
paralelipiped cu dimensiunile 7, 10, 30:
int p[7][10][30];
2
PC 1 – CURSUL 5 SILVESTRU CĂTĂLIN
3
PC 1 – CURSUL 5 SILVESTRU CĂTĂLIN
Atunci când poziția elementului crește cu 1, poziția simetricului scade cu 1. Prin urmare, suma
dintre poziția elementului și poziția simetricului este constantă (n-1). Astfel deduce că simetricul
elementului v[i] este v[n-i-1].
4
PC 1 – CURSUL 5 SILVESTRU CĂTĂLIN
Metoda aceasta este cea mai „convenabilă” în cazul în care vectorul nu este ordonat. Dacă
vectorul este ordonat, în loc de o căutare secvențială poate fi efectuată o căutare binară. Această metodă
implică lucrul prin împărțiri succesive ale vectorului. Primul pas îl reprezintă compararea elementului
căutat cu elementul din mijlocul vectorului. În cazul cel mai fericit în care corespunde, căutarea se
oprește. Dacă nu, se verifică dacă elementul căutat este mai mic ori mai mare decât elementul din mijloc.
Astfel, se alege una dintre cele două alternative posibile: se caută în continuare la stânga elementului
din mijloc ori la dreapta lui. Spre exemplu, în format cod, aplicarea acestei metode de căutare asupra
vectorului v de n elemente ordonat crescător este următoarea:
for(st=0, dr=n-1, gasit=0; !gasit && st<=dr; )
{
mijloc=(st+dr)/2;
if(v[mijloc]==x)
gasit=1;
else
if(v[mijloc]<x)
st=mijloc+1;
else
dr=mijloc-1;
}
if(gasit)
printf(“%i se gaseste pe pozitia %i\n”, x, mijloc);
else
printf(“%i nu se afla in vector”, x);
5
PC 1 – CURSUL 5 SILVESTRU CĂTĂLIN
6
PC 1 – CURSUL 5 SILVESTRU CĂTĂLIN
do{
schimb=0; //variabilă semafor ce indică efectuarea unei modificări
for(i=0; i<n; i++)
if(v[i]>v[i+1])
{
aux=v[i];
v[i]=v[i+1];
v[i+1]=aux;
schimb=1;
}
}while (schimb);
7
PC 1 – CURSUL 5 SILVESTRU CĂTĂLIN
#include <stdio.h>
#define m 1000
#define nrmax 10000
5.2.9. Interclasarea
Prin interclasare se înțelege combinarea a două mulțimi ordonate de elemente într-o singură
mulțime ordonată. Spre exemplu, doi vectori - a și b – cu n și respectiv m elemente ordonate crescător,
sunt interclasați într-un vector c.
O soluție oarecum simplă dar ineficientă presupune copierea celor doi vectori în vectorul c, iar
mai apoi efectuarea unei operații de sortare a vectorului c.
O altă soluție mai eficientă implică parcurgerea simultană a vectorilor a și b și compararea la
fiecare pas a elementului curent din vectorul a cu elementul curent din vectorul b. Elementul considerat
cel mai mic este apoi copiat și plasat în vectorul c, avansarea fiind apoi efectuată doar în vectorul din
care a fost preluat elementul. La epuizarea elementelor unui vector, elementele rămase din celălalt se
copiază integral în continuare în c fără a mai fi comparate.
8
PC 1 – CURSUL 5 SILVESTRU CĂTĂLIN
9
PC 1 – CURSUL 5 SILVESTRU CĂTĂLIN
Parcurgerea diagonalelor
O matrice pătratică, asemenea unei forme geometrice de tip pătrat, prezintă două diagonale:
diagonala principala (ce pleacă de la primul element al matricei – M[0][0] – până la ultimul element al
matricei – M[n-1][n-1]) și diagonala secundară (ce pleacă de la ultimul element de pe prima linie –
M[0][n-1] – până la primul element de pe ultima linie – M[n-1][0]).
Astfel, pe diagonala principală, indicele de linie este egal cu indicele de coloană; elementele
aflate pe aceasta fiind: M[0][0], M[1][1], M[2][2], …, M[n-1][n-1].
Afișarea diagonalei principale se poate realiza cu ajutorul unei singure instrucțiuni for:
for(i=0; i<n; i++)
print(“M[%d][%d]=%d “, i, i, M[i][i]);
Pe diagonala secundară, suma dintre indicele de linie și indicele de coloană este constant, suma
acestora fiind egală cu n-1. Prin urmare, elementele aflate pe aceasta sunt: M[0][n-1], M[1][n-2], …,
M[n-1][0].
Afișarea diagonalei secundare se poate realiza de asemenea cu ajutorul unei singure instrucțiuni
for:
for(i=0; i<n; i++)
printf(“M[%d][%d]=%d “, i, n-i-1, M[i][n-i-1]);
10
PC 1 – CURSUL 5 SILVESTRU CĂTĂLIN
Asemenea oricărei variabile, variabila de tip șir de caractere poate fi inițializată chiar de la
declarare, cu o constantă șir (caz în care, lungimea șirului Ls poate lipsi, fiind determinată automat):
char sir1[]=”Hello world”;
char sir2[30]=”Bye world”;
În exemplul de față, lungimea lui sir1 a fost determinată automat – 12 octeți (câte un octet
pentru fiecare caracter + 1 pentru marcajul de sfârșit de șir).
Șirurile de caractere pot fi prelucrate la nivel de caracter (parcurse element cu element,
asemenea vectorilor) ori la nivel de structură (cu ajutorul anumitor funcții disponibile în bibliotecile
limbajului de programare).
Atenție! Constantele șir de caracter sunt marcate între ghilimele (“a”), pe când constantele
caracter sunt marcate între apostrofuri (‘a’).
În acest caz, în șirul s se vor citi toate caracterele până la primul spațiu. De exemplu, pentru
șirul “Hello world”, șirul s va fi Hello. Se recomandă atenție atunci când numărul de caractere până la
primul spațiu este mai mare decât lungime declarată a șirului, deoarece se pot distruge date iar erorile
de program sunt asigurate.
Pentru a putea citi toate datele introduse la tastatură ci nu doar până la primul spațiu, apare în
ajutor biblioteca istream. În cadrul acestei sunt definite două funcții pentru citirea caracterelor și a
șirurilor de caractere: get() și getline(). Pentru a putea fi utilizate, este necesară specificarea fluxului de
intrare din care se citește și numele funcției, separate prin operatorul de selecție ‘.’: cin.get() ori
cin.getline().
Funcția getline() are următorul format:
getline(char *s, int n, char c=’\n’);
Primul parametru este reprezentat de șirul în care se realizează citirea. Al doilea parametru
reprezintă numărul maxim de caractere ce se dorește a fi citit. Al treilea parametru (opțional) este un
caracter delimitator (dacă nu este inclus se consideră implicit ‘\n’). Prin urmare, funcția va citi caractere
de la intrare până la întâlnirea caracterului delimitator ori până când a citit exact n-1 caractere.
Spre exemplu, dacă este necesară citirea de la tastatură a tuturor caracterelor până la întâlnirea
caracterului ‘.’, se apelează funcția getline() astfel:
cin.getline(s, 100, ‘.’);
11
PC 1 – CURSUL 5 SILVESTRU CĂTĂLIN
Funcția get() are mai multe forme de apel. Dacă se dorește utilizarea acesteia pentru citirea
șirurilor de caractere, apelul și efectul este asemănător cu funcția getline(), diferența fiind faptul că
getline() extrage din fluxul de intrare caracterul delimitator, în timp ce get() nu îl extrage.
Apelarea funcției get fără nici un parametru returnează un singur caracter citit de la intrare, iar
cu un parametru de tip char va returna valoarea caracterului citit de la intrare.
Se scriu astfel caracterele din șir, până la întâlnirea primului marcaj de sfârșit de șir (NULL).
Spre exemplu, dacă șirul s este “Hello world and \n welcome!” se va afișa:
Hello world and
welcome!
Dacă în schimb șirul s este “Hello world \0 and welcome!”, va fi scris până la caracterul NULL
(\0).
De observat faptul că nu a fost transmis funcției scanf() adresa șirului s (&s). Acest lucru se
datorează faptului că numele oricărui vector este adresa primului element din vector.
Prin urmare, se vor citi primele caractere care încep cu primul caracter diferit de un caracter
alb, până la întâlnirea unui caracter alb. Dacă numărul de caractere citite este mai mare decât lungimea
declarată a șirului, programul va rezulta la rulare o eroare.
Indicarea în specificatorul de format cu un număr determină limitarea numărului de caractere
care se citesc:
scanf(“%7s”, s);
Funcția scanf() nu permite citirea șirurilor de caractere ce conțin caractere albe. Pentru a putea
citi șiruri ce conțin spații cât și caractere tab se poate utiliza funcția gets(), declarată în header-ul stdio.h.
Prototipul acestei funcții este:
char *gets(char *s);
12
PC 1 – CURSUL 5 SILVESTRU CĂTĂLIN
Prin urmare, pentru a realiza citirea unui șir de caractere ce conține și caractere tab sau spații
se apelează funcția gets() astfel:
gets(s);
De asemenea, în header-ul stdio.h este declarată funcția puts() ce afișează pe ecran toate
caracterele șirului s (incluzând spațiul și tab) până la întâlnirea caracterului de linie nouă (\n). Funcția
returnează de asemenea o valoare pozitivă în cazul afișării cu succes; în caz contrar, returnează valoarea
-1. Formatul funcției este următorul:
puts(s);
13
PC 1 – CURSUL 4 SILVESTRU CĂTĂLIN
Cursul 4
FIȘIERE TEXT ÎN C/C++
Un fișier reprezintă o colecție de date de același tip, memorate pe un suport extern (HDD, SSD,
CD, USB-stick etc.).
Fișierele permit memorarea unui volum mare de date persistente (nu se „pierd” la închiderea
programului ori a calculatorului). Acest lucru reprezintă avantajul principal în utilizarea fișierelor.
Aplicațiile prezentate până la acest moment au avut la bază lucrul cu date citite de la tastatură,
memorate în variabile simple. Acestea sunt memorate și alocate în memoria RAM a sistemului de
calcul, iar memoria alocată se eliberează fie pe parcursul execuției unui program, fie la închiderea
programului. Prin urmare, toate datele memorate în cadrul acelor variabile se pierd prin ștergerea ori
suprascrierea acestora în cadrul memoriei. Astfel, este nevoie ca datele necesare în cadrul rulării unui
program să fie introduse din nou și din nou de la tastatură l-a fiecare rulare a acestuia de către cel ce îl
rulează. Aici intervin fișierele! Acestea sunt special concepute pentru a rezolva această problemă. În
loc de a introduce datele de la tastatură, programul își poate procura singur datele dintr-un fișier de
fiecare dată când este rulat.
Există două tipuri de fișiere după care este clasificat conținutul acestora:
- fișiere text – conțin secvențe de caractere ASCII structurate pe linii;
- fișiere binare – conțin secvențe de octeți, fără o structură predefinită.
Declararea fișierelor
Pentru ca un program să poată prelua și stoca date într-un fișier, este necesară asocierea
fișierului respectiv cu un stream de intrare/ieșire; asemenea stream-urilor cin și cout asociate pentru
citirea de la tastatură, respectiv afișarea pe ecran.
În primul rând, pentru a putea realiza acest lucru este necesară includerea header-ului
fstream.h, unde sunt declarate clasele ifstream, ofstream și fstream. Primul lucru necesar în
utilizarea operațiilor de intrare/ieșire într-un program folosind fișiere, este de a declara variabilele de
tipurile ifstream, ofstream și fstream:
ifstream – declarare stream de intrare (dedicat doar operațiilor de citire);
ofstream – declarare stream de ieșire (dedicat doar operațiilor de scriere);
fstream – declarare stream de intrare/ieșire (dedicat atât operațiilor de citire cât și de
scriere, în funcție de modul specificat la deschidere).
1
PC 1 – CURSUL 4 SILVESTRU CĂTĂLIN
Deschiderea fișierelor
Pentru a putea utiliza un stream declarat, acesta trebuie mai întâi deschis. La deschidere, stream-
ul este asociat unui fișier fizic și, eventual, se precizează modul de deschidere care determină operațiile
permise cu fișierul respectiv. Deschiderea poate fi realizată în două moduri:
după declarare, prin apelul funcției open, precizând ca parametri un șir de caractere ce
reprezintă specificatorul fișierului, conform sintaxei sistemului de operare utilizat și,
eventual modul de deschidere;
la declarare, specificând după numele stream-ului declarat numai parametrii
corespunzători funcției open, încadrați între paranteze rotunde.
Pentru tipul ifstream se utilizează implicit modul de citire ios::in, iar pentru tipul
ofstream se utilizează implicit modul de scriere ios::out. Pentru tipul fstream este obligatoriu a
fi specificat modul de deschidere al fișierului.
În acest exemplu este declarat un stream de intrare numit f1, deschis mai apoi cu ajutorul
funcției open și asociat cu fișierul info.in. Deoarece nu este precizată locația din memoria externă pe
care se află fișierul ori calea folder-ului în care se află acesta, se deduce că fișierul se află în folder-ul
curent din memoria externă.
Exemplul 2:
ofstream f2;
f2.open(“c:\\wrk\\fisiere\\info.out”);
2
PC 1 – CURSUL 4 SILVESTRU CĂTĂLIN
Aici a fost declarat un stream de ieșire numit f2, asociat cu fișierul info.out, aflat pe discul
c:, în folder-ul fisiere, subfolder al folder-ului wrk. Nefiind prezentă specificarea modului de
deschidere, se va utiliza modul de deschidere implicit – crearea unui fișier gol cu numele specificat,
dedicat doar operațiilor de scriere.
Exemplul 3:
fstream f3;
f3.open(“info.in”, ios::in);
Se declară un stream numit f3, asociat fișierului info.in. Din moment ce tipul fstream nu
are asociat un mod de deschidere implicit, a fost adăugată precizarea modului de deschidere ca
ios::in, fișierul deschis fiind astfel un fișier de intrare dedicat numai operaților de citire.
Exemplul 4:
fstream f4;
f4.open(“info.out”, ios::out);
Un exemplu asemănător celui anterior, diferența fiind faptul că de această dată, modul de
deschidere al fișierului a fost declarat ca ios::out, fișierul deschis fiind astfel un fișier de ieșire
dedicat numai operaților de scriere.
Dacă după operația de deschidere a unui fișier, variabila corespunzătoare are valoarea NULL,
atunci operația de deschidere a eșuat. De aceea, este recomandat să se testeze succesul operației de
deschidere înainte de a efectua orice alte operații cu fișierul.
În acest exemplu se citește din fișierul info.in primul caracter, indiferent dacă acesta este
caracter alb sau nu.
3
PC 1 – CURSUL 4 SILVESTRU CĂTĂLIN
4
PC 1 – CURSUL 4 SILVESTRU CĂTĂLIN
Declararea fișierelor
Operațiile de intrare/ieșire în C au fost exemplificate până acum cu ajutorul instrucțiunilor
scanf() și printf(). Acestea permit preluarea de informații de la tastatură, respectiv afișarea
acestora pe ecran. Pentru a prelua și a scrie date într-un fișier, trebuie mai întâi declarat fișierul prin
indicarea numelui acestuia, precum și operațiile permise asupra conținutului fișierului respectiv.
În header-ul stdio.h se găsesc funcțiile, constantele, tipurile de date și variabilele globale ce
fac posibilă prelucrarea informațiilor în fișiere. Pentru a putea utiliza într-un program operații de
citire/scriere în fișiere este necesară declararea a cel puțin o variabilă de tip FILE *, declarație realizată
astfel:
FILE * variabila_fisier;
Deschiderea fișierelor
Odată ce variabila pentru fișier a fost declarată, este necesară cunoașterea modului în care vor
fi prelucrate datele din fișier. Pentru aceasta se utilizează funcția fopen(), astfel:
variabila_fisier = fopen(const char *nume, const char *mod);
Se începe prin declararea fișierelor, în acest caz trei la număr. Urmează apoi deschiderea
acestora:
f1 = fopen(“info.in”, “r”);
Fișierul de intrare f1 a fost deschis și asociat cu fișierul info.in. Nefiind precizată locația pe
disc a fișierului, se deduce că fișierul se află în folder-ul curent din memoria externă.
f2 = fopen(“info.out”, “w”);
ori
f2 = fopen(“c:\\wrk\\fisiere\\info.out”, “w”);
5
PC 1 – CURSUL 4 SILVESTRU CĂTĂLIN
Fișierul de ieșire f2 a fost deschis și asociat fișierului info.out. În acest exemplu sunt
prezentate atât varianta în care nu este precizată locația, respectiv varianta în care este precizată locația
în memorie.
f3 = fopen(“info.txt”, “a”);
În acest exemplu a fost deschis fișierul de intrare f3 și asociat cu fișierul info.txt. Modul a
reprezintă modul de adăugare de date (append), astfel încât, dacă există deja date în fișier, noile date
introduse vor fi plasate imediat după cele deja existente.
Dacă după operația de deschidere a unui fișier, variabila corespunzătoare are valoarea NULL,
atunci operația de deschidere a eșuat. De aceea, este recomandat să se testeze succesul operației de
deschidere înainte de a efectua orice alte operații cu fișierul. De exemplu:
#include <stdio.h>
int main(){
FILE *f;
if(!(f=fopen(“date.in”, “r”)))
printf(“Eroare la deschiderea fisierului!\n”);
return 0;
}
Prototipul funcției fscanf() este asemănător cu prototipul funcției scanf(), diferența fiind
apariția în plus a variabilei fișierului. Semnificația celorlalți parametrii, rezultatul și efectul funcției este
același în ambele funcții.
Este valabilă aceeași observație precizată anterior la funcția de citire. Funcția fprintf() este
asemănătoare cu funcția printf(), diferența fiind prezența variabilei fișierului.
6
PC 1 – CURSUL 4 SILVESTRU CĂTĂLIN
De asemenea, există și varianta utilizării funcției feof(). Aceasta va returna valoarea 0 dacă
nu a ajuns la sfârșitul fișierului, respectiv o valoare diferită de zero dacă s-a ajuns la sfârșitul fișierului.
Închiderea fișierelor
După ce au fost realizate toate operațiile necesare asupra unui fișier, acesta trebuie închis pentru
a se evita eventuale modificări nedorite ori apariția erorilor. Închiderea unui fișier se realizează prin
apelarea funcției fclose(), astfel:
fclose(variabila_fisier);
7
PC 1 – CURSUL 4 SILVESTRU CĂTĂLIN
return 1;
}
if(!(f2=fopen(“copie.txt”, “w”)))
{
printf(“Eroare la deschiderea fisierului de iesire”);
return 2;
}
while(!feof(f1))
if(fscanf(f1, “%c”, &x) != EOF)
fprintf(f2, “%c”, x);
fclose(f1);
fclose(f2);
return 0;
}
8
PC 1 – CURSUL 4 SILVESTRU CĂTĂLIN
}
for(i=0; i<n; i++)
{
er = fscanf(f1, “%u”, &x);
if(!er)
{
printf(“Eroare la citirea valorilor\n”);
return 3;
}
fprintf(f2, “%u”, x);
d=2;
while(x>1)
{
p=0;
while(x%d==0)
{
p++;
x/=d;
}
if(p)
fprintf(f2, “(%d, %d)”, d, p);
d++;
}
fprintf(f2, “\n”);
}
fclose(f1);
fclose(f2);
return 0;
}
9
PC 1 – CURSUL 3 SILVESTRU CĂTĂLIN
Cursul 3
INSTRUCȚIUNILE LIMBAJULUI C/C++
Limbajele de programare cuprind instrucțiuni ce implementează complet conceptele proiectării
structurate și modularizate a programelor. Acestea desemnează acțiuni care se aplică datelor pentru
obținerea rezultatelor necesare din rularea unui algoritm.
Instrucțiunile se regăsesc sub forma unor secvențe de apeluri, instrucțiune ce poate avea diverse
configurații cod mașină, în funcție de tipul operanzilor pe care îi referă. Instrucțiunile unui program în
C/C++ se grupează într-un bloc delimitat de o pereche de acolade ( { } ). După modul de realizare a
construcțiilor sintactice și al numărului de acțiuni descrise, pot fi instrucțiuni simple și instrucțiuni
structurate. De asemenea, pot exista blocuri de instrucțiuni executabile, denumite instrucțiuni compuse.
O instrucțiune simplă descrie o singură acțiune, unic determinată ce nu provoacă condiționări.
Din această categorie fac parte instrucțiunile: goto, break, continue și instrucțiunea vidă.
O instrucțiune compusă este o secvență de instrucțiuni (simple, structurate sau compuse)
delimitată de perechea de acolade. Aceasta implementează natural structura secvențială din
programarea structurată.
O instrucțiune structurată conține alte instrucțiuni (simple, compuse sau structurate) ce sunt
executate alternativ sau repetitiv. Prin cadrul acestor instrucțiuni se codifică structurile fundamentale
alternative sau repetitive din algoritm.
Orice instrucțiune din limbajul C/C++ de termină cu caracterul ’;’!
Instrucțiunea vidă
Aceasta nu are o mnemonică explicită, fiind dedusă din contextul anumitor construcții
sintactice. Aceasta se folosește acolo unde trebuie să apară o instrucțiune, care însă nu va executa nimic,
situație întâlnită frecvent în cazul instrucțiunilor structurate.
De exemplu, în cazul unei structuri decizionale de tip adevărat/fals (if), unde este prezentă o
instrucțiune sau expresie doar pe ramura negativă, cea negativă fiind completată de o instrucțiune vidă.
if (a>b)
;
else
a=0;
1
PC 1 – CURSUL 3 SILVESTRU CĂTĂLIN
Instrucțiunea expresie
Aceasta evaluează o expresie ce poate fi un calcul matematic, o incrementare, o atribuire ori
chiar apelul unei funcții. De asemenea, este permis ca expresia să fie vidă, devenind astfel o instrucțiune
vidă. În plus, este permisă utilizarea oricărei expresii sintactice corecte, indiferent dacă aceasta
generează sau nu un efect (ex: 1+3;).
Exemple de instrucțiune expresie:
int a;
b=1;
i++;
break;
Instrucțiunea goto
Este o instrucțiune de salt necondiționat. Forma acesteia este:
goto etichetă;
unde eticheta reprezintă un identificator urmat de caracterul ’:’, urmat apoi de o instrucțiune simplă
sau compusă. Rolul etichetei este de a defini punctul de salt către care se face trimitere prin utilizarea
instrucțiunii goto. Etichetele au valabilitate locală, nefiind astfel posibilă realizarea unui salt din corpul
unei funcții în alta.
Instrucțiunea break;
Este folosită numai în cadrul unei instrucțiuni structurate; efectul acestei instrucțiuni fiind de
terminare imediată a execuției instrucțiunii respective.
Instrucțiunea continue;
Poate fi folosită numai în interiorul unui ciclu, prin care se abandonează iterația curentă și se
trece la următoarea.
2
PC 1 – CURSUL 3 SILVESTRU CĂTĂLIN
Ca efect, expresia va fi evaluată, expresie ce poate fi de orice tip. Dacă valoarea expresiei este
diferită de 0 (adevărată), se execută instrucțiune_1, în caz contrar se execută instrucțiune_2.
Expresiile se vor încadra întotdeauna între paranteze rotunde!
Instrucțiunile pot fi de tip simple ori compuse, precum:
if (expresie)
{
instrucțiune_1;
...
instrucțiune_n;
}
else
instrucțiune_m;
3
PC 1 – CURSUL 3 SILVESTRU CĂTĂLIN
De asemenea, dacă instrucțiune_2 este vidă, ramura else poate să nu mai fie inclusă,
obținându-se astfel o formă simplificată a instrucțiunii if:
if (expresie)
instrucțiune;
În locul unei structuri de tipul if-then-else se poate utiliza operatorul condițional ’?:’,
atunci când instrucțiune_1 și instrucține_2 sunt de tip expresie.
4
PC 1 – CURSUL 3 SILVESTRU CĂTĂLIN
Ca efect, expresie este evaluată. Se compară apoi succesiv valoarea expresiei cu valorile
expresiilor constante care etichetează alternativele case. Dacă se întâlnește o alternativă case
etichetată cu valoarea expresiei, se execută secvența de instrucțiuni corespunzătoare și toate secvențele
de instrucțiuni care urmează, până la întâlnirea instrucțiunii break; ori până la întâlnirea acoladei
închise ce marchează sfârșitul instrucțiunii switch. Dacă nici una dintre valorile etichetelor
alternativelor case nu coincide cu valoarea expresiei, se execută secvența de instrucțiuni etichetată
default.
Expresia va fi încadrată întotdeauna între paranteze rotunde!
O secvență de instrucțiuni poate fi vidă, prin urmare, dacă instrucțiunea etichetată default
este vidă, ramura default poate lipsi (asemănător ramurii else).
5
PC 1 – CURSUL 3 SILVESTRU CĂTĂLIN
Ca efect, se va evalua expresia, apoi, dacă valoarea acesteia este 0, se iese din instrucțiunea
while. Dacă valoarea expresiei este diferită de 0, se execută instrucțiunea, apoi se revine la evaluarea
expresiei.
6
PC 1 – CURSUL 3 SILVESTRU CĂTĂLIN
Ca efect, se va executa instrucțiunea, iar apoi se va evalua expresia. Dacă valoarea expresiei
este 0, se va ieși din structura repetitivă, în caz contrar, se revine la primul pas – executarea instrucțiunii.
Utilizarea instrucțiunii continue; în instrucțiunile while și do-while are ca efect
abandonarea iterației curente și trecerea la evaluarea expresiei ce controlează terminarea ciclului.
În acest exemplu, dacă variabila n avea valoarea 0, iar în loc de instrucțiunea do-while ar fi
fost utilizată instrucțiunea while, algoritmul nu ar fi funcționat corect, deoarece variabila nr ar fi rămas
cu valoarea 0, pe când numărul n=0 are o cifră!
7
PC 1 – CURSUL 3 SILVESTRU CĂTĂLIN
8
PC 1 – CURSUL 3 SILVESTRU CĂTĂLIN
În a doua variantă, instrucțiunea utilizată este instrucțiunea vidă, toate prelucrările fiind descrise
cu ajutorul celor 3 expresii care intervin în instrucțiunea for.
De menționat faptul că, oricare dintre cele 3 expresii pot fi vide, separatorul ’;’ fiind în
continuare obligatoriu să apară în instrucțiunea for. Este necesară însă includerea unei instrucțiuni
break; în acest caz. Spre exemplu:
for ( ; ; )
{
cin>>n;
if (n>0) break;
}
În acest exemplu se va citi de la tastatură valori ce sunt alocate variabilei n. Atunci când
valoarea citită este mai mare strict decât 0, instrucțiunea for va fi încheiată.
9
PC 1 – CURSUL 2 SILVESTRU CĂTĂLIN
Cursul 2
TIPURI DE DATE, BIBLIOTECI,
CITIRE/SCRIERE ȘI EXPRESII
Datele, în general, reprezintă tot ceea ce este prelucrat de către un sistem de calcul. Datele pot
fi de mai multe tipuri prin definirea mulțimii valorilor pe care le pot lua, modul de reprezentare a
acestora în memorie și operațiile ce se pot efectua cu acestea. Mulțimea valorilor unui anumit tip de
date reprezintă constantele tipului respectiv!
Există în cadrul oricărui limbaj de programare un set de tipuri de date predefinite (standard)
ce depind de implementarea limbajului utilizat. Noi vom avea în prim-plan implementarea Borland
C++ 3.1. În cazul utilizării unui alt mediu de programare (Linux, Java etc.) se va observa existența a
altor tipuri de date predefinite, precum și diferențe de implementare. Acestea fiind spuse, iată ce tipuri
de date vom folosi în C/C++:
De asemenea, datele pot avea mai multe „înfățișări”. Putem întâlni o constantă care să fie
scrisă în mai multe feluri, dar cu același înțeles pentru sistemul de calcul, deoarece acesta respectă
sistemul de numerație. Un sistem de numerație este determinat de o mulțime finită de simboluri ce
respectă un set de reguli de reprezentare a numerelor cu ajutorul simbolurilor respective. Numărul de
simboluri constituie baza sistemului de numerație.
Constantele sunt reprezentate de numere din intervalul corespunzător tipului de date. Acestea
pot fi precizate în mai multe baze, precum:
Fiecare cifră dintr-un număr valorează de b ori mai mult decât cifra din dreapta sa, astfel
încât cifrele numărului – de la dreapta la stânga – corespund pozițional puterilor bazei
sistemului de numerație:
1
PC 1 – CURSUL 2 SILVESTRU CĂTĂLIN
Operații de conversie
Pe baza regulii de reprezentare a numerelor într-o bază, putem realiza conversia numerelor de
la o baza în alta.
Pentru a realiza conversia unui număr din baza 10 într-o bază b se vor realiza împărțiri
succesive la b până când se obține câtul 0. În primul pas se convertește deîmpărțitul; în continuare la
fiecare pas, câtul de la împărțirea precedentă devenind deîmpărțit. Reprezentarea numărului în baza b
se obține considerând resturile rezultate în ordinea inversă obținerii lor. Spre exemplu, dorim să
convertim numărul 3717(10) în baza 8. Vom realiza împărțiri succesive la 8:
3717 = 8 * 464 + 5
464 = 8 * 58 + 0
58 = 8 * 7 + 2
7 = 8 * 0 + 7
Prin urmare, considerând resturile în ordine inversă, se obține 3717(10) = 7205(8).
Pentru a realiza conversia unui număr subunitar din baza 10 într-o bază b se vor realiza
înmulțiri succesive cu b până când se obține o perioadă ori până când partea fracționară a rezultatului
este 0. În primul pas se convertește deînmulțitul; în continuare la fiecare pas, partea fracționară a
rezultatului precedent devenind deînmulțit. Reprezentarea numărului în baza b se obține considerând
cifrele de la partea întreagă a rezultatelor în ordinea obținerii lor. Spre exemplu, dorim să convertim
numărul 0,625(10) în baza 2. Vom realiza înmulțiri succesive cu 2:
0,625 x 2 = 1,25
0,25 x 2 = 0,5
0,5 x 2 = 1,0
Prin urmare, considerând partea întreagă în ordinea obținerii lor rezultă 0,625(10) = 0,101(2).
Pentru a realiza conversia unui număr real din baza 10 într-o bază b se vor converti separat
partea întreagă și partea fracționară, conform regulilor prezentate anterior. Spre exemplu numărul
3717,625(10) (cele două numere exemplificate mai sus, combinate) scris în bază 2:
3717,625(10) = 111010000101,101(2)
2
PC 1 – CURSUL 2 SILVESTRU CĂTĂLIN
Pentru a realiza conversia unui număr din baza b în baza 10 se va utiliza dezvoltarea
numărului după puterile bazei sistemului de numerație, urmând apoi a se efectua calculele. Spre
exemplu, vom realiza conversia unui număr din baza hexazecimală și a unuia din bază binară în baza
10:
Pentru a realiza conversia unui număr din baza b în baza b` (b, b`∈ℕ, b, b`>1), este
necesară trecerea numărului prin baza 10. Se va realiza mai întâi conversia numărului din baza
b în baza 10, iar apoi din baza 10 în baza b`.
Dimensiunea reprezentării datelor de tip int poate fi de 8, 16, 32 ori 64 de poziții binare, în
funcție de domeniul de valori reprezentat:
numerele întregi mai mari ca 0 (pozitive) sunt codificate simplu prin reprezentarea lor
binară;
numerele întregi mai mici ca zero (negative) sunt codificate prin complementarea
codificării valorii lor absolute față de 2x, unde x reprezintă dimensiunea reprezentării.
Prin urmare, prima poziție din reprezentare indică semnul (0 pentru numere pozitive și 1
pentru numere negative); spre exemplu, vom reprezenta numărul întreg -247:
28 = 1 0 0 0 0 0 0 0 0 -
1 1 1 1 0 1 1 1 (247)
0 0 0 0 1 0 0 1 (-247)
3
PC 1 – CURSUL 2 SILVESTRU CĂTĂLIN
După cum se poate observa din explicația de mai sus, tipul de date int conține și numere
naturale (numere întregi pozitive), acestea fiind denumite date de tip unsigned și long (acesta din
urmă are rolul de a mării dimensiunea reprezentării); obținându-se astfel următoarele tipuri de date
întregi:
Pentru a facilita scrierea tipului de date în cadrul algoritmilor, se poate utiliza un sufix
corespunzător tipului dorit:
Sufix Tip
u, U unsigned
l, L long
ul, UL, uL, UL unsigned long
Tipul char
Acest tip de date este de asemenea de tip întreg, reprezentat pe un octet și ce prezintă semn.
Prezintă un singur modificator – unsigned char.
Constantele de tip char/unsigned char pot fi numere întregi din intervalul de valori specificat
ori caracterele ce au codurile ASCII din intervalul specificat; datele de tip char având astfel o dublă
natură – caractere ASCII și numere întregi. Pentru sistemul de calcul nu este nimic ambiguu,
caracterul ‘A’ fiind pentru acesta același lucru cu constanta 65.
Caracterele grafice prezentate în tabelul ASCII pot fi specificate prin încadrarea caracterului
respectiv între apostrofuri (ex: ‘a’, ‘4’, ‘*’, ‘ ‘, ‘$’).
Există caractere speciale negrafice ce au deja asociate secvențe escape, alcătuite din
backslash și un caracter sugestiv:
4
PC 1 – CURSUL 2 SILVESTRU CĂTĂLIN
Secvență
Caracter
escape
‘\b’ Caracterul backspace - deplasează cursorul pe ecran cu o poziție spre stânga
‘\t’ Caracterul tab - deplasează cursorul pe ecran cu un tab orizontal
‘\n’ Caracterul newline - deplasează cursorul pe o linie nouă
‘\a’ Caracterul alarm - generează un sunet
‘\\’ Caracterul backslash
‘\’’ Caracterul apostrof
‘\”’ Caracterul ghilimele
Tipul real
Tipul real de date în limbajul C/C++ sunt definite ca float și double, dintre care double
acceptă și modificatorul long.
Constantele de tip real ce se pot reprezenta în memoria sistemului de calcul sunt reprezentate
de numere raționale din intervalele specificate, acestea fiind posibil a fi specificate în notația uzuală
ori în format exponențial (științific). În forma uzuală se precizează partea întreagă și zecimală a
numărului, separate prin caracterul punct. În format exponențial se poate preciza în plus și un
exponent al lui 10, precedat de litera e sau E. Valoarea constantei se obține prin înmulțirea numărului
cu 10 la puterea specificată. Spre exemplu:
5
PC 1 – CURSUL 2 SILVESTRU CĂTĂLIN
Tipul void
Tipul de date void este un tip special, deoarece mulțimea valorilor acestuia este vidă. Acest tip
este utilizat atunci când este necesară specificarea absenței oricărei valori (de exemplu, în cazul
funcțiilor ce nu au nici o dată de ieșire, precum a fost prezentat mai devreme în cazul funcției
main()).
Variabile
Variabilele sunt date ce își pot modifica valoarea pe parcursul execuției unui program. Înainte
de a putea utiliza o variabilă, aceasta trebuie declarată prin specificarea numelui și tipului acesteia și,
eventual, alocarea unei valori inițiale ce se dorește a fi atribuită variabilei. Formatul de declarare al
unei variabile este:
Observații:
- parantezele (‘[‘ ‘]’) au rolul de a încadra elementele opționale în cadrul declarării unei
variabile;
- variabila poate avea ca și tip de date oricare dintre tipurile prezentate mai sus
- numele variabilei are rolul de identificator, prin urmare acesta trebuie să fie unic (de
reamintit informațiile prezentate anterior la subcapitolul de identificatori);
- este posibilă declararea multiplă a variabilelor de același tip prin simpla separare a
numelor acestora prin virgulă, asemenea cum este prezentat opțional în exemplul de
declarare;
- pentru a atribui o valoare unei variabile încă de la declarare, trebuie specificat după
numele acesteia prin intermediul caracterului ‘=’ și o expresie de inițializare ce trebuie
să fie evaluabilă în momentul declarării.
O variabilă poate fi declarată atât în interiorul unei funcții (spre exemplu între acoladele
funcției main()), ori în exteriorul oricărei funcții. Dacă declarația este plasată în interiorul unei
funcții, aceasta va lua denumirea de variabilă locală; în celălalt caz, va lua denumirea de variabilă
globală. Una dintre diferențele majore între aceste două tipuri de variabile este aceea că variabilele
globale sunt automat inițializate cu 0, pe când cele locale, nu sunt inițializate.
Între limbajul C și C++ există o diferență când vine vorba despre poziția declarării
variabilelor în cadrul unei funcții. În C++ ne este permisă plasarea declarației de variabilă oriunde în
corpul unei funcții. În C, declarațiile trebuie plasate obligatoriu la începutul corpului funcției, înaintea
oricărei instrucțiuni.
6
PC 1 – CURSUL 2 SILVESTRU CĂTĂLIN
char c;
double x=b*0.5, y;
Am declarat trei variabile de tip int (a, b, c), o variabilă de tip char (c) și două variabile de
tip număr real/double (x, y). Variabila b este inițializată cu valoarea 7, variabila c are atribuită
valoarea 3 iar variabila x este inițializată cu valoarea 3.5. Variabilele a, c și y nu au atribuite nici o
valoare inițială de declarare.
2.2. Preprocesare
Directivele preprocesor încep cu caracterul ’#’. Spre exemplu: #include, #define, #if,
#line etc. Pentru început ne vom axa pe 2 dintre cele mai importante și uzual folosite directive:
#include și #define.
Directiva #include realizează exact ce arată, include într-un program un fișier antet
standard ori unul creat de utilizator. Fișierele antet (uzual numite „header”) conțin declarațiile
funcțiilor, constantelor, variabilelor și tipurilor definite într-o bibliotecă de date. Fișierul antet este
specific bibliotecii pe care dorim să o utilizăm în program și are întotdeauna extensia h. Numele
fișierelor antet standard vor fi încadrate între paranteze unghiulare (‘<’ și ‘>’), iar fișierele create de
utilizatori vor fi încadrate între ghilimele.
#include <nume_fisier_antet.h>
//respectiv
#include “nume_fisier_antet.h”
7
PC 1 – CURSUL 2 SILVESTRU CĂTĂLIN
Directiva #define permite definirea unei constante simbolice, adică, preprocesorul va substitui
în program orice apariție a identificatorului de constantă cu valoarea acestuia (poate fi gândită și ca un
fel de alocare a unei variabile, doar că nu are aceleași proprietăți). Pentru a apela o astfel de directivă
se procedează astfel:
Constantele simbolice sunt utile atunci când se dorește asocierea unei denumiri sugestive unei
valori, oferind o lizibilitate mai bună asupra algoritmului. Un alt efect este faptul că programul devine
mai ușor de modificat, astfel încât, dacă o valoare definită astfel se modifică, nu mai este necesară
parcurgerea și modificarea în algoritm a acestei valori, ci doar în cadrul directivei de definire.
#define PI 3.14
#define e 2.71
#define NotaMAX 10
în fișierul antet values.h sunt definite spre exemplu următoarele constante simbolice:
o MAXINT – ce are ca valoare cel mai mare număr de tip int = 32767;
o MINLONG – ce are ca valoare cel mai mic număr de tip long = -2147483648;
în fișierul antet conio.h sunt definite constante simbolice pentru culori:
o BLACK – cu valoarea 0;
o BLUE – cu valoarea 1;
o GREEN – cu valoarea 2 etc.
Există în limbajul C/C++ unele operații frecvent utilizate, cum ar fi: citirea, scrierea,
ștergerea ecranului, extragerea radicalului ș.a.m.d.; operații pentru care nu există instrucțiuni
specifice. Se pot ajunge la acestea prin implementarea instrucțiunilor deja existente în limbaj, însă
acest lucru ar fi total ineficient de realizat de fiecare dată când trebuie rezolvată o problemă. De aceea
au fost constituite bibliotecile de funcții, ce au în compoziție funcții de utilitate generală, grupate pe
categorii de utilizare. Este necesară doar includerea fișierului antet al bibliotecii și apelarea funcției
necesare.
Prin urmare, pentru a putea utiliza funcțiile dintr-o bibliotecă este necesară includerea la
începutul programului header-ul ce conține declarațiile funcțiilor existente în biblioteca respectivă.
8
PC 1 – CURSUL 2 SILVESTRU CĂTĂLIN
Pentru ca funcția să fie apelată este necesară cunoașterea numelui și formatului acesteia.
Bibliotecile pot conține zeci ori chiar sute de funcții, ceea ce este imposibil de a le cunoaște ori reține
pe toate. De aceea, pentru a putea extrage doar funcțiile necesare în cadrul rezolvării unei probleme,
fie se poate apela sistemul de autodocumentare al mediului de programare (Help) ori putem inspecta
biblioteca spre a găsi funcția necesară (pentru cei mai puternici și răbdători). Fiecare funcție conține
niște parametrii similari argumentelor funcțiilor matematice, astfel încât, pentru apelarea funcției mai
este necesară și specificarea valorilor efective ale parametrilor pentru care este apelată funcția.
Valorile parametrilor de la apel trebuie să fie în corespondență ca număr, ordine și tip cu parametrii
specifici funcției. Formatul unui astfel de apel de funcție se prezintă astfel:
nume_funcție (listă_valori_parametri);
conio.h – conține o funcție ce are drept scop ștergerea ecranului (mai exact a ferestrei
curente). Apelarea acestei funcții se realizează astfel;
void clrscr(void);
Mai devreme am prezentat cum în conio.h sunt definite și constante simbolice pentru
culori. Prin urmare, cu ajutorul acestei biblioteci putem apela o funcție ce ne permite
schimbarea culorii de fundal a ecranului (implicit negru):
textbackground(BLUE);
clrscr();
9
PC 1 – CURSUL 2 SILVESTRU CĂTĂLIN
Prin citirea, respectiv scrierea datelor, înțelegem operații de intrare/ieșire a datelor. Citirea
datelor reprezintă operația prin care se introduc în una sau mai multe variabile valori introduse de la
tastatură ori extrase de pe un suport de memorare extern (fișiere). Scrierea datelor reprezintă operația
de afișare pe ecran a datelor prelucrate ori de stocare a acestora în memorie/fișiere. Pe moment ne
vom axa doar pe citirea de la tastatură și afișarea pe ecran.
În limbajul C/C++, citirea și scrierea datelor se realizează prin intermediul unor funcții
existente în bibliotecile standard ale limbajului (mai exact iostream.h). Diferențele de intrare/ieșire
dintre C și C++ sunt majore, în C++ fiind orientate pe obiect operațiile de citire/afișare.
Citirea în C++
Pentru a introduce date de la tastatură se va folosi pe lângă declararea funcției de intrare și
setul de caractere specific operatorului de extragere (‘>>’), astfel:
cin>>nume_variabilă;
Prin urmare, va fi citită o succesiune de caractere introduse de la tastatură, care mai apoi va fi
convertită într-o dată de tipul variabilei specificate, în final fiind atribuită valoarea către variabilă.
Operatorul de extragere/citire poate fi utilizat de mai multe ori pe aceeași linie de execuție
pentru a citi succesiv mai multe variabile în acel moment al programului:
cin>>nume_variabilă_1>>nume_variabilă_2>>...>>nume_variabilă_n;
10
PC 1 – CURSUL 2 SILVESTRU CĂTĂLIN
int main(){
int a, b;
cin>>a;
cin>>b;
return 0;
În acest exemplu a fost mai întâi apelată biblioteca necesară pentru realizarea operației de
citire (iostream.h). După, în cadrul funcției main(), am declarat două variabile (a și b) și le-am
introdus niște valori citite de la tastatură. Am fi putut de asemenea să scriem secvența de citire sub
forma:
cin>>a>>b;
Scrierea în C++
Pentru realizarea operației ce afișare, este necesară utilizarea șirului de caractere ‘<<’, numit
și operatorul de inserție/ieșire; utilizarea fiind realizată astfel:
cout<<expresie;
cout<<expresie_1<<expresie_2<<...<<expresie_n;
int a;
int main(){
int b;
cout<<”b=”; cin>>b;
11
PC 1 – CURSUL 2 SILVESTRU CĂTĂLIN
return 0;
În limbajul C, față de C++, citirea și scrierea datelor se realizează prin intermediul unor
funcții specifice.
Citirea în C
Pentru a citi date de la tastatură în C, este necesară folosirea unei funcții cu un format
specificat: scanf(). Această funcție permite citirea datelor într-un format specificat. Pentru a putea fi
folosită, este necesară apelarea bibliotecii stdio.h. Formatul funcției este următorul:
Atenție! Dacă în timpul apelării funcției de citire nu este specificată adresa variabilei (lipsa
operatorului de referință), funcția va citi de la tastatură valoarea, însă nu o va atribui nici-unei
variabile.
12
PC 1 – CURSUL 2 SILVESTRU CĂTĂLIN
%[*][lg][l|L]literă_tip
Se poate observa că orice specificator conține obligatoriu caracterul ’%’, urmat de o literă tip
ce indică tipul de date al valorii care se citește. Caracterul ’%’ are un rol important, mai exact de a
indica începutul unui specificator de format. Între acestea două se poate adăuga opțional alte elemente
(marcate între parantezele drepte). Literele tip pot fi:
Literă_tip Semnificație
d Indică citirea în memorie a unei valori de tip int în baza 10
(zecimal/decimal)
o Indică citirea în memorie a unei valori de tip int în baza 8 (octal)
x Indică citirea în memorie a unei valori de tip int în baza 16
(hexazecimal)
u Indică citirea în memorie a unei valori de tip unsigned în baza 10
f, e ori g Indică citirea în memorie a unei valori de tip real (float) în baza 10
c Indică citirea în memorie a unui caracter (char)
(în acest caz, caracterele albe prezente la intrare nu se ignoră)
s Indică citirea unui șir de caractere
(șirul începe de la primul caracter care nu este alb și se termină la
primul caracter alb întâlnit, ori până la epuizarea dimensiunii maxime
lg din specificatorul de format)
Opțional poate fi inclus în cadrul parametrului format, între cele două caractere obligatorii,
caracterele prezentate anterior în paranteze; rolul acestora fiind:
litera l ori L provine de la termenul long. Caracterul l poate fi utilizat împreună cu literele
d, o și x pentru a converti valoarea citită la tipul long int; cu litera u pentru a converti
valoarea citită la tipul unsigned long și cu literele f, e ori g pentru a converti valoarea citită
la tipul double. Caracterul L se poate folosi doar cu f, e ori g pentru a converti valoarea
citită la tipul long double.
lg este folosit pentru a specifica lungimea maximă a zonei de citire. Astfel, funcția
scanf() va citi maxim lg caractere ori până la întâlnirea unui caracter alb ori până la
întâlnirea cu un caracter neconvertibil în tipul specificat.
caracterul ’*’ specifică faptul că la intrare este prezentă o dată de tipul specificat de acest
specificator de format, însă ea nu va fi atribuită pentru nici o variabilă dintre cele specificate
în lista de parametrii ai funcției scanf().
13
PC 1 – CURSUL 2 SILVESTRU CĂTĂLIN
Dacă se dorește ca la citire să apară caracterul ’%’, trebuie să utilizăm construcția sintactică
’%%’ în cadrul parametrului format.
Pentru citirea caracterelor exisă 2 funcții speciale: getch() și getche(). Ambele funții fac
parte din biblioteca conio.h și îndeplinesc același rol – citesc de la tastatură un caracter iar apoi
returnează codul ASCII al caracterului citit. Diferența dintre cele 2 funcții este aceea că getch()
citește caracterul și nu îl afișează, pe când getche() îl citește și îl afișează pe ecran.
int getch(void);
int getche(void);
Exemple de citire în C
Fie următoarele declarații de variabile:
int a; unsigned b; double c;
variabila a va primi valoarea 123, variabila c va primi valoarea -2.1 iar variabila b va primi valoarea
900000 (atenție la ordinea variabilelor în funcție, de la tastatură fiind introduse în altă ordine!)
variabilei C1 i se va atribui caracterul ’p’, variabilei C2 caracterul ’i’, iar variabilei C3 caracterul ’3’.
variabilei n i se atribuie valoarea 421, variabilei C1 caracterul ’ ’, iar variabilei C2 caracterul ’x’.
variabilei n i se atribuie valoarea 421, variabilei C1 caracterul ’x’, iar variabilei C2 caracterul ’D’.
(Concluzie: atenție la spațierea parametrilor format!)
14
PC 1 – CURSUL 2 SILVESTRU CĂTĂLIN
variabilei x i se va atribui valoarea 4, variabilei y valoarea 1 iar variabilei z valoarea 2. (Chiar dacă
ecuația introdusă este incorectă, funcția de citire nu poate verifica acest lucru, însă putem face un
program în continuare ce va verifica valorile introduse în variabile pentru a decide corectitudinea
ecuației.)
variabila x va avea valoarea 4 iar variabila y valoarea 2. Caracterul 1 a fost citit, însă nu a fost atribuit
nici unei variabile.
variabila zi va prelua valoarea 28, luna valoarea 01 iar anul valoarea 1999.
Afișarea în C
Afișarea datelor pe ecran se realizează cu ajutorul funcției printf(), funcție declarată în
stdio.h, cu următoarea sintaxă:
Prin apelarea funcției printf(), expresiile vor fi evaluate și afișate pe ecran în ordine și în
forma specificată de parametrul format.
Parametrul format, asemenea celui din scanf(), reprezintă un șir de caractere ce conține
specificatori de format și, opțional, alte caractere. În cadrul acestuia trebuie să existe câte un
specificator de format pentru fiecare expresie din lista de parametrii. Specificatorii de format au
următoarea sintaxă:
%[lg][.prec][l|L]literă_tip;
15
PC 1 – CURSUL 2 SILVESTRU CĂTĂLIN
Literă_tip Semnificație
d Afișarea unei expresii de tip int în baza 10 (zecimal/decimal)
o Afișarea unei expresii de tip int în baza 8 (octal)
x, X Afișarea unei expresii de tip int în baza 16 (hexazecimal)
(caracterele A-F sunt afișate sub formă de majuscule pentru X ori minuscule pentru x)
u Afișarea unei expresii de tip unsigned în baza 10
f Afișarea unei expresii de tip float în format parte_întreagă.parte_zecimală
e, E Afișarea unei expresii de tip float în format exponențial cifra.parte_zecimalăe(ori E)±exponent
g Se aplică tipul e ori f, varianta care reprezintă un număr minim de caractere
c Se afișează caracterul al cărui valoare din expresie se găsește în codul ASCII
s Expresia este afișată sub forma unui șir de caractere
Opțional poate fi inclus în cadrul parametrului format, între cele două caractere obligatorii, caracterele
prezentate anterior în paranteze; rolul acestora fiind:
litera l ori L provine de la termenul long. Caracterul l poate fi utilizat împreună cu literele
d, o și x ori X pentru a converti expresia la tipul long int; cu litera u pentru a converti
expresia la tipul unsigned long și cu literele f, e, E ori g pentru a converti expresia la tipul
double. Caracterul L se poate folosi doar cu e, E ori g pentru a converti expresia la tipul
long double;
lg este folosit pentru a specifica lungimea minimă a zonei de afișare. Astfel, funcția
printf() va afișa minim lg caractere, îndeplinind necesarul minim prin completarea cu
spații libere în stânga (alinierea textului fiind la dreapta);
prin utilizarea specificatorului .prec putem specifica precizia de afișare a expresiilor de
tip real ori a unui șir de caractere. În cazul numerelor reale, se indică numărul de zecimale
a se dori fi afișat (implicit 6 zecimale). În cazul în care precizia este mai mică decât numărul
de zecimale al valorii reale de afișat, valoarea va fi rotunjită prin adunare la ultima
zecimală. În cazul șirurilor de caractere, precizia va limita numărul de caractere din șir ce
va fi afișat.
Exemple de afișare în C
char c=’E’;
16
PC 1 – CURSUL 2 SILVESTRU CĂTĂLIN
Acum că avem pe ce variabile să lucrăm, putem efectua diverse apeluri ale funcției
printf(). Va fi prezentat în paralel cu fiecare apelare ceea ce se va afișa pe ecran:
Apelare Ecran
printf("x=%d\n", x); x=-10
printf("x=%u\n", x); x=65535
y=1234 ori y=2322 ori
printf("y=%d ori y=%o ori y=%X\n", y, y, y); y=4D2
printf("a=%f\n", a); a=123.456000
printf("a=%e\n", a); a=1.234560e+2
printf("a=%.2f\n", a); a=123.45
printf("b=%f ori\n b=%E ori\n b=%g\n", b, b, b); b=-0.070000 ori
b=-7.000000E-2 ori
b=-7e-2
printf("c=%c ori c=%d\n", c, c); c=E ori c=69
printf("z=%d ori\n ori z=%x ori z=%u\n", z, z, z); z=-20453
ori z=b01a ori 45082
2.6. Expresii
Expresia reprezintă o succesiune de operanzi conectați prin operatori. Operanzii pot avea
rolul de constantă, o variabilă, o expresie ori chiar apelul unei funcții. Operatorii desemnează
operațiile ce se execută asupra operanzilor, grupați pe categorii în funcție de tipul operațiilor
desemnate.
Evaluarea unei expresii presupune calculul valorii expresiei, timp în care se ține cont de
existența parantezelor, de asociativitate și de prioritatea operațiilor, astfel:
- prima dată se evaluează expresiile din paranteze, începând cu parantezele cele mai din
interior (în matematică se folosește succesiunea de paranteze ()→[]→{}, însă în limbajul
de programare se vor folosi doar paranteze rotunde);
- în lipsa parantezelor se vor efectua operațiile după ordinea priorității operatorilor (va fi
prezentat în rândurile care urmează așa numita clasă de prioritate);
- dacă într-o expresie apar succesiuni de operatori cu priorități egale, se va ține cont de
asociativitatea operatorilor. În C/C++, operatorii se asociază de la stânga la dreapta,
excepție fiind operatorii unari, condiționali și de atribuire ce se asociază de la dreapta la
stânga (urmează a fi prezentați pe larg acești operatori).
Cei mai importanți operatori sunt cei aritmetici, de incrementare/decrementare, egalitate,
relaționali, logici, de atribuire, condiționali și de conversie.
Operatorii mai sunt clasați, pe lângă tipul operațiilor, pe clase de prioritate. Există 16 clase
de prioritate, numerotate de la 1 la 16 (1 fiind clasa cu prioritate maximă). Pe parcursul prezentării
categoriilor tipurilor acestora, vom prezenta și clasele din care fac parte.
17
PC 1 – CURSUL 2 SILVESTRU CĂTĂLIN
În limbajul C/C++, operatorii se pot aplica pe unul sau doi operanzi (prin urmare pot fi unari
ori binari), printre care, toți operatorii unari vor fi încadrați în clasa de prioritate 2.
Operatori de atribuire
Acești operatori binari permit modificarea valorii unei variabile prin intermediul a 11
operatori:
variabilă = expresie;
este echivalent cu
long r=101000;
float x, y=7;
18
PC 1 – CURSUL 2 SILVESTRU CĂTĂLIN
Operatorul virgulă
Acest operator binar permite compunerea mai multor expresii, astfel încât să fie tratate din
punct de vedere sintactic ca o singură expresie. Formatul unei astfel de expresii compuse este:
Acest tip de expresie este utilizată atunci când sintaxa permite evaluarea unei singure
expresii, însă este necesară evaluarea mai multor expresii. Clasa de prioritate a acestui operator este
16.
Operatorii aritmetici
Operatori desemnați operațiilor matematice uzuale (adunare ’+’, scădere ’-’, înmulțire ’*’,
împărțire ’/’, restul împărțirii la întreg ’%’ și semnul algebric pozitiv/negativ ’+’/’-’). Operatorul ’%’
poate fi aplicat doar operanzilor de tip int; iar operatorul ’/’, poate fi aplicat operanzilor de tip int și
float, cu funcții diferite: dacă ambii operanzi sunt întregi, operatorul va furniza câtul întreg al
împărțirii; dacă unul dintre operanzi este real, operatorul va furniza rezultatul împărțirii reale.
19
PC 1 – CURSUL 2 SILVESTRU CĂTĂLIN
float r=1.5;
Operatori de incrementare/decrementare
Aceștia sunt operatori unari ce au ca efect mărirea (incrementarea – ’++’) respectiv
micșorarea (decrementarea – ’--’) valorilor operandului cu 1. Aceștia se pot utiliza fie înaintea
operandului – caz în care se va efectua întâi incrementarea/decrementarea și apoi utilizarea valorii
operandului – ori după operand – caz în care se va utiliza mai întâi valoarea operandului iar apoi
efectuarea incrementării/decrementării. Acest tip de operatori pot fi utilizați doar în cadrul variabilelor
simple, nefiind capabili a fi utilizați cu o expresie (ex: (a+b)--;). După cum a fost precizat anterior,
deoarece sunt operatori unari fac parte din clasa de prioritate 2.
unsigned b=4;
float c=3.2;
Operatori de egalitate
Aceștia sunt operatori binari ce au ca efect desemnarea relației de egalitate (’==’) ori
inegalitate (’!=’) în care se găsesc doi operanzi. Rezultatul obținut în urma utilizării acestor operatori
poate fi doar de tipul „adevărat” (1) sau „fals” (0), în funcție de rezultatul relației operanzilor. Acești
operatori fac parte din clasa de prioritate 8.
20
PC 1 – CURSUL 2 SILVESTRU CĂTĂLIN
Operatori raționali
Aceștia sunt operatori binari ce au ca efect desemnarea relației de ordine în care se găsesc doi
operanzi:
Operatori condiționali
Acești operatori binari sunt reprezentați de caracterele ’?’ respectiv ’:’ și se folosesc
întotdeauna împreună. Formatul în care se utilizează acești operatori este:
Ca efect, expresie_1 este evaluat, iar dacă valoarea acestuia este nenulă, atunci valoarea
expresiei condiționale este egală cu valoarea expresie_2. Dacă expresie_1 este nul, atunci
valoarea expresiei condiționale este egală cu valoarea expresie_3.
21
PC 1 – CURSUL 2 SILVESTRU CĂTĂLIN
&variabilă
(tip) expresie
De reținut faptul că într-o expresie în care operanzii sunt de același tip, tipul expresiei va
coincide cu tipul acestora. În cazul în care tipul operanzilor este diferit, pe parcursul efectuării
expresiei se vor realiza automat o serie de conversii implicite. Regula acestor conversii implicite este:
operandul care are un domeniu de valori mai restrâns este convertit la tipul operandului care are
mulțimea valorilor mai amplă.
sizeof (expresie);
sizeof (tip);
22
PC 1 – CURSUL 2 SILVESTRU CĂTĂLIN
x 0 ≠0 && 0 ≠0 || 0 ≠0
!x 1 0 0 0 0 0 0 1
≠0 0 1 ≠0 1 1
23
PC 1 – CURSUL 2 SILVESTRU CĂTĂLIN
x 0 1 & 0 1 ^ 0 1 | 0 1
~x 1 0 0 0 0 0 0 1 0 0 1
1 0 1 1 1 0 1 1 1
Operatorii logici de deplasare a reprezentării binare au ca efect mutarea primului operand
spre stânga (’<<’) ori spre dreapta (’>>’). Numărul de poziții după care se deplasează este specificat
de al doilea operand. Acest operand este întâlnit sub forma „x<<n” ori „x>>n”, ce are ca efect
înmulțirea operandului x cu 2n, respectiv împărțirea întreagă a operandului x cu 2n.
În urma efectuării deplasării la stânga, pozițiile rămase libere în partea dreaptă se vor
completa cu 0. În cazul efectuării deplasării la dreapta, pozițiile rămase libere în partea stângă se vor
completa cu 0 (dacă avem de a face cu un operand întreg pozitiv) ori cu 1 (dacă operandul este întreg
negativ).
Operațiile pe biți sunt cel mai rapid tip de operații, determinând o eficiență ridicată la
executarea programului.
int a=0X202C
a= 0 0 1 0 0 0 0 0 0 0 1 0 1 1 0 0
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
int b=0XF81C
b= 1 1 1 1 1 0 0 0 0 0 0 1 1 1 0 0
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
~a= 1 1 0 1 1 1 1 1 1 1 0 1 0 0 1 1
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
a&b= 0 0 1 0 0 0 0 0 0 0 0 0 1 1 0 0
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
24
PC 1 – CURSUL 2 SILVESTRU CĂTĂLIN
a^b= 1 1 0 1 1 1 1 1 1 1 1 1 0 0 1 1
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
a|b= 1 1 1 1 1 0 0 0 0 0 1 1 1 1 0 0
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
a>>3= 0 0 0 0 0 1 0 0 0 0 0 0 0 1 0 1
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
a<<3= 0 0 0 0 0 0 0 1 0 1 1 0 0 0 0 0
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
b>>3= 1 1 1 1 1 1 1 1 0 0 0 0 0 0 1 1
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
Se poate observa în cazul operației de deplasare la dreapta faptul că variabila a este un număr
întreg pozitiv (completare la stânga cu 0) iar variabila b este un număr întreg negativ (completare la
stânga cu 1).
25
PC 1 – CURSUL 1 SILVESTRU CĂTĂLIN
Cursul 1
BAZELE SISTEMELOR DE CALCUL ȘI A
LIMBAJULUI C/C++
Sistemul de calcul
Un sistem de calcul (sau calculator) este reprezentat ca fiind o structură destinată prelucrării
datelor, acesta fiind alcătuit din două subsisteme: hardware și software. În mod concret, un SC este
format din 3 mari resurse: fizice, logice și informaționale.
Hardware-ul este reprezentat de totalitatea componentelor electronice ce alcătuiesc partea
fizică a SC, definind astfel resursele fizice (procesor, memorie, dispozitive de intrare/ieșire ș.a.m.d.).
Software-ul constă în instrucțiuni și date ce sunt prelucrate de către SC spre executarea
diverselor cerințe ale utilizatorului. Astfel definim resursele logice, care l-a rândul său se împart în
software de sistem și software de aplicații. Soft-ul de sistem cuprinde aplicațiile de sistem de uz comun,
precum: sistemul de operare, compilatoarele etc. Soft-ul de aplicații cuprinde aplicațiile propriu-zise:
programele utilizatorilor, utilitare, medii de programare, jocuri etc. De reținut de asemenea faptul că o
secvență de instrucțiuni formează un program, iar datele prelucrate de către SC constituie o bază de
date.
Ultimul tip de resurse, cele informaționale, reprezintă suporturi de memorie externă pentru
organizarea și stocarea datelor (HDD, SSD etc.).
În cadrul unui SC, resursele fizice și cele logice sunt în permanentă cooperare spre satisfacerea
cerințelor utilizatorului în ceea ce privește introducerea/recepționarea datelor, memorarea datelor și
informațiilor, prelucrarea acestora, transmiterea informațiilor între alte sisteme de calcul și regăsirea
informațiilor. Îndeplinirea acestor operații trebuie realizată de unitățile funcționale ale SC. Acestea sunt
conectate atât fizic cât și logic între ele, fiind individualizabile prin funcția specifică a fiecăreia în cadrul
unui sistem de calcul.
Sistemele de calcul folosite în prezent funcționează prin efectuarea unor operații logice folosind
un flux de impulsuri electrice ori optice, reprezentând formule binare formate din 1 sau 0. Acestea au
denumirea de biți. Un SC stochează și procesează informația cu ajutorul tranzistorilor astfel: un semnal
de tip 0 este caracterizat prin trecerea libera a curentului electric prin tranzistor, pe când nepermiterea
trecerii va fi caracterizată de un semnal de tip 1.
1
PC 1 – CURSUL 1 SILVESTRU CĂTĂLIN
Bazele arhitecturii unui SC sunt în continuare aceleași încă din 1940, atunci când John von
Neumann (matematician austro-ungar) a creat principiile arhitecturii mașinilor de calcul contemporane.
Principiile arhitecturii von Neumann au rămas neschimbate indiferent de îmbunătățirile dramatice ale
design-ului și performanțelor calculatoarelor de-a lungul ultimului secol.
Arhitectura von Neumann descrie un SC prin intermediul a patru module importante:
Acestea sunt interconectate prin intermediul unor magistrale ce fac posibilă transmiterea datelor
de calcul și a instrucțiunilor, totul fiind însă coordonat de frecvența unui ceas prin transmiterea
permanentă de impulsuri regulate.
UAL reprezintă „creierul” oricărui SC, scopul acestui fiind de a realiza operații aritmetice, de
comparație, de manevrare a datelor și de influențare a ordinii instrucțiunilor.
UCC este modul central ce leagă și comandă toate celelalte module între ele, rolul acesteia fiind
de a citi instrucțiunile și datele din memorie ori de la dispozitivele de intrare/ieșire, să le decodifice, să
ofere către UAL datele de intrare corecte conform instrucțiunii, să comande UAL ce operații să
efectueze și de asemenea, să ofere rezultatele în memorie ori către dispozitivele de ieșire.
Aceste două module împreună alcătuiesc Unitatea Centrală de Procesare (CPU) – procesorul
– care stă la baza oricărui SC.
2
PC 1 – CURSUL 1 SILVESTRU CĂTĂLIN
Cât despre memorie, aceasta poate fi văzuta ca fiind un șir ce celule numerotate, fiecare având
o adresă proprie, precum casele de pe o stradă. Aceste celule pot înmagazina (memora) o cantitate
prestabilită de informație (fie o instrucțiune ori chiar date propriu-zise) și de o dimensiune limitată.
Locația de memorie este cea mai mică zonă de memorie adresabilă, aceasta fiind constituită din 8 celule
binare consecutive. Cantitatea de informație stocată într-o succesiune de 8 biți se numește octet (byte).
Capacitatea memoriei este reprezentată de numărul total de octeți (bytes) ce pot fi înregistrați
în memorie. Capacitatea memoriei se exprimă prin multiplii ai byte-ului astfel:
1 Kb (kilobyte) = 1024 bytes (210 bytes);
1 Mb (megabyte) = 1024 Kb (220 bytes);
1 Gb (gigabyte) = 1024 Mb (230 bytes);
1 Tb (terabyte) = 1024 Gb (240 bytes).
Sistemele de intrare/ieșire (I/O) sunt dispozitivele ce se conectează la un SC pentru a face
posibilă preluarea de informații și de a raporta înapoi rezultatele în mediul extern (ex: mouse, tastatură,
ecran, imprimantă ș.a.m.d.).
Performanțele unui sistem de calcul reprezintă probabil cel mai important criteriu atunci când
alegem un calculator. Acestea se apreciază în funcție de:
- viteza de execuție – dată de numărul instrucțiunilor executate într-o unitate de timp;
- capacitatea de memorie – dată de numărul de octeți ce pot fi memorați atât în memoria
internă cât și în cea externă;
- viteza de acces la memorie – timpul necesar obținerii unui octet de informație memorat;
- fiabilitatea – numărul de defecte (fie ele hardware ori software) ce apar într-un interval de
timp de utilizare;
- tipul arhitecturii – modalitatea de funcționare a sistemelor (multiprogramare, acces la
distanță, multiprocesare)
Limbajul de programare
Limbajul de programare se poate defini ca fiind un mijloc de comunicare între programator și
calculator. Există trei aspecte ce definesc un limbaj de programare:
3
PC 1 – CURSUL 1 SILVESTRU CĂTĂLIN
Setul de caractere
Pentru a facilita comunicarea dintre programator și calculator, a fost creat un set de caractere
ce stă la baza codului-mașină: setul de caractere al codului ASCII. ASCII este acronimul pentru
American Standard Code for Information Interchange (în traducere „Codul Standard American pentru
Schimbul de Informații).
Această codificare a caracterelor constă în asocierea în mod unic a fiecărui caracter (cifre, litere,
semne speciale, simboluri grafice) o valoare numerică – un cod format din 7 cifre binare (prin urmare
o valoare numerică din mulțimea {0, 1, 2, ..., 127}). Pentru reprezentarea unor caractere grafice și a
unor caractere speciale (simboluri matematice, diacritice ori caractere specifice altor limbi) se utilizează
8 biți pentru codificarea caracterelor, fiind astfel posibilă codificarea a 256 de caractere și simboluri
distincte.
Acest set de caractere este utilizat pentru scrierea programelor C/C++ și reprezintă mai exact
un sistem de codificare a caracterelor, bazat pe alfabetul englez. Codurile ASCII sunt des întâlnite sub
forma unui tabel ce conține cele 256 de caractere, câte un caracter pe octet:
4
PC 1 – CURSUL 1 SILVESTRU CĂTĂLIN
5
PC 1 – CURSUL 1 SILVESTRU CĂTĂLIN
Cod Caracter Cod Caracter Cod Caracter Cod Caracter Cod Caracter Cod Caracter
128 Ç 150 û 172 ¼ 194 ┬ 216 ╪ 238 ∈
129 ü 151 ù 173 ¡ 195 ├ 217 ┘ 239 ∩
130 é 152 ÿ 174 « 196 ─ 218 ┌ 240 ≡
131 â 153 Ö 175 » 197 ┼ 219 █ 241 ±
132 ä 154 Ü 176 ░ 198 ╞ 220 ▄ 242 ≥
133 à 155 ¢ 177 ▒ 199 ╟ 221 ▌ 243 ≤
134 å 156 £ 178 ▓ 200 ╚ 222 ▐ 244 ⌠
135 ç 157 ¥ 179 │ 201 ╔ 223 ▀ 245 ⌡
136 ê 158 ₧ 180 ┤ 202 ╩ 224 α 246 ÷
137 ë 159 ƒ 181 ╡ 203 ╦ 225 ß 247 ≈
138 è 160 á 182 ╢ 204 ╠ 226 Γ 248 °
139 ï 161 í 183 ╖ 205 ═ 227 π 249 ●
140 î 162 ó 184 ╕ 206 ╬ 228 Σ 250 -
141 ì 163 ú 185 ╣ 207 ╧ 229 σ 251 √
142 Ä 164 ñ 186 ║ 208 ╨ 230 µ 252 ⁿ
143 Å 165 Ñ 187 ╗ 209 ╤ 231 τ 253 ²
144 É 166 ª 188 ╝ 210 ╥ 232 Φ 254 ■
145 æ 167 º 189 ╜ 211 ╙ 233 Θ 255
146 Æ 168 ¿ 190 ╛ 212 ╘ 234 Ω
147 ô 169 ⌐ 191 ┐ 213 ╒ 235 δ
148 ö 170 ¬ 192 └ 214 ╓ 236 ∞
149 ò 171 ½ 193 ┴ 215 ╫ 237 φ
6
PC 1 – CURSUL 1 SILVESTRU CĂTĂLIN
1.2. Identificatori
Identificatorii joacă un rol cheie în programare. Deseori fiind asociați sub denumirea de
„nume”, rolul acestora este de a denumi elemente ale programului, precum: variabile, constante, funcții
ș.a.m.d.; posibilitatea de a distinge acestea de alte date și la care se poate face referire în cadrul
proceselor de prelucrare. Un identificator este constituit, din punct de vedere sintactic, dintr-o
succesiune de caractere de tipul litere, cifre ori ‘_’ (underscore), primul caracter fiind obligatoriu o
literă sau underscore.
Exemple de identificatori:
Corect: Incorect:
Adresa a-b (conține caracterul „+”)
_3 5a (începe cu o cifră)
Există de asemenea alte câteva aspecte importante de luat în vedere în ceea ce privește lucrul
cu identificatorii:
Limbajul C/C++ ține cont de diferența dintre literele mari și cele mici (este case-sensitive).
Prin urmare, putem utiliza 2 identificatori de genul ABc și Abc, aceștia fiind diferiți.
Identificatorul poate avea orice lungime, fiind luate în considerare însă doar primele 31 de
caractere.
Pentru a facilita și ușura atât scrierea cât și citirea unui program, este recomandat ca
identificatorii să aibă un „nume” sugestiv, eventual fiind apelate comentarii pentru a fi
explicați rolul acestora. Putem utiliza pentru un identificator prescurtarea unui cuvânt ori
acronime (exemplu: NrTel – (Număr telefon), NPA – (Nume Prenume Adresa) etc.).
În limbajul C/C++ există o serie de cuvinte rezervate (keywords), ce au rolul de
identificatori speciali. Aceștia au un înțeles predefinit și pot fi utilizați numai în
construcțiile sintactice în care sunt specificați și vor fi scriși întotdeauna numai cu litere
mici. Aceștia sunt prezentați în tabelul următor:
7
PC 1 – CURSUL 1 SILVESTRU CĂTĂLIN
1.3. Comentarii
În programare, documentarea joacă un rol esențial! Chiar dacă scriem un program spre uz
personal ori spre a-l da mai departe, avem nevoie de a scrie o „documentație” ce explică cum
funcționează. Această documentație poate fi de tip externă (un fișier separat de tip articol, raport ori
manual) sau de tip internă sub forma unor comentarii inserate printre liniile de program. Astfel, putem
insera după fiecare secvență de program câte un comentariu ce face referire la scopul și funcționarea
secvenței respective, prezentarea tipurilor de date utilizate, a variabilelor ș.a.m.d. Programele vor deveni
din ce în ce mai încărcate și complexe, astfel încât existența unor astfel de comentarii nu va aduce decât
beneficii precum: o citire și înțelegere mai ușoară a programului, cât și facilitarea întreținerii și
modificării acestuia atât de programator, cât și de către utilizatori.
Comentariile i-au forma unui text simplu ce va fi ignorat de către compilator, acestea fiind
„vizibile” doar de către operatorul uman către care sunt adresate. Pentru a putea realiza un astfel de
comentariu, trebuie respectate câteva reguli simple din punct de vedere sintactic:
- dacă comentariul ocupă o singură linie, este necesar ca la începutul acestuia să apară două
slash-uri „ // ”. Prin urmare, comentariul va începe imediat după această succesiune de
caractere și se va termina la sfârșitul liniei.
- în cazul în care avem un comentariu mai lung ce se întinde pe mai multe rânduri, trebuie
introduse 2 succesiuni de caractere în cadrul cărora va fi încadrat comentariul. Prima
succesiune este reprezentată de un slash și un asterisc „ /* „ ce indică începutul
comentariului, a doua succesiune fiind reprezentată de un asterisc și un slash „ */ „ , lucru
ce indică încheierea comentariului.
8
PC 1 – CURSUL 1 SILVESTRU CĂTĂLIN
Exemple de comentarii
//acesta este un comentariu și se termină la sfârșitul liniei
/*acesta este un comentariu ce poate fi scris pe mai multe linii, fiind
încheiat la întâlnirea succesiunii de simboluri: */
1.4. Separatori
universali: reprezentați de caracterele albe precum spațiul (` `), tab-ul (`\t`), sfârșitul de
linie/linie nouă (`\n`) și comentariile;
specifici/delimitatori: în declararea variabilelor, acestea sunt separate prin caracterul
virgulă (`,`), delimitarea sfârșitului unei instrucțiuni ori al unei declarații prin intermediul
caracterului punct și virgulă (`;`), caracterul apostrof ( ` ) ce delimitează o constantă
caracter și ghilimelele (`” ”`) ce delimitează un șir de caractere.
Un algoritm este descris ca fiind o secvență finită de etape clare, prin intermediul căruia,
indiferent dacă există sau nu date de intrare, acesta trebuie să furnizeze cel puțin o valoare de ieșire.
Pentru a putea implementa pe un sistem de calcul un algoritm, operațiile acestuia trebuie să fie definite
și efective; adică, trebuie cunoscute exact numărul finit de etape ce sunt necesar a fi executate, într-un
timp rezonabil de lung.
Rezolvarea unei probleme constă în parcurgerea a trei etape fundamentale:
1. Declararea și citirea datelor de intrare specifice problemei necesar a fi
rezolvată;
2. Prelucrarea datelor de intrare pe baza unor modele matematice cu scopul
obținerii valorilor datelor de ieșire;
3. Afișarea valorilor rezultate, ce reprezintă soluția problemei și implicit
finalizarea algoritmului.
9
PC 1 – CURSUL 1 SILVESTRU CĂTĂLIN
Există o varietate largă de reprezentare a algoritmilor de rezolvare, cele mai populare fiind:
textual, prezentarea pas-cu-pas, prin ilustrarea cu ajutorul schemelor logice, arbori de programare,
tabele de decizie și pseudocoduri. Vom parcurge unele dintre ele câte puțin pentru a realiza o imagine
de ansamblu a modalităților de reprezentare a algoritmilor.
Un aspect important în rezolvarea problemelor este iterativitatea. Aceasta reprezintă procesul
prin care rezultatul unei etape de rezolvare poate fi obținut după parcurgerea repetată a unui set de
operații, de fiecare dată cu alte valori de intrare. Numărul de iterații poate fi cunoscut ori necunoscut,
dar întotdeauna determinabil pe parcursul execuției și, trebuie să fie întotdeauna finit.
Astfel ia naștere conceptul de recursivitate. Acesta reprezintă procesul iterativ prin care
determinăm valoarea unei variabile pe baza uneia sau mai multor proprietăți ai valorii anterioare.
Deoarece valoarea curentă a variabilei este dependentă de una sau mai multe valori anterioare, procesul
poate fi unirecursiv sau multirecursiv.
Un proces recursiv presupune următoarele aspecte:
- definirea unei formule de start;
- declararea valorii inițiale a parametrului ce intră în procesul recursiv;
- declararea formulei recursive;
- declararea și/sau restricționarea numărului de cicluri de calcul.
V2=V1+1;
...
Vi=Vi-1+1;
...
Vn=Vn-1+1.
10
PC 1 – CURSUL 1 SILVESTRU CĂTĂLIN
11
PC 1 – CURSUL 1 SILVESTRU CĂTĂLIN
B. Structuri decizionale (alternative) – conțin obligatoriu cel puțin un predicat și cel puțin un bloc
funcțional (excepție fiind blocul START). Structurile decizionale pot avea următoarele forme:
- IF (c; s1, s2) – dacă c atunci execută s1 altfel s2 (selecție simplă);
- IF (c; s1) – dacă c atunci execută s1 (pseudoalternativă);
- CASE (v1, v2, ..., vn; s1, s2, ..., sn) – dacă v1 atunci execută s1, dacă v2 atunci execută s2,
..., dacă vn atunci execută sn (structură alternativă multiplă).
12
PC 1 – CURSUL 1 SILVESTRU CĂTĂLIN
Figura 1.3. structuri decizionale: a) selecție simplă, b) pseudoalternativa, c) structura alternativă multiplă
C. Structuri repetitive – frecvent întâlnite și sub denumirea de structuri de tip ciclu ori structuri
iterative. Acestea trebuie să conțină obligatoriu un bloc predicativ și un bloc funcțional care se
execută de un număr finit de ori (până când predicatul își schimbă valoarea). Structurile
repetitive pot avea următoarele forme:
- WHILE (c; s) – cât timp condiția c este adevărată se execută s, apoi se continuă cu
blocul următor ori cu următoarea instrucțiune (ciclu cu test inițial);
- REPEAT (c; s) – repetă s până când condiția c este adevărată, apoi continuă cu
instrucțiunea următoare (ciclu cu test final);
- FOR (v≤vf; v, s, vr) – este similară cu structura WHILE, diferența fiind faptul că
aceasta este alcătuită din trei blocuri ce îndeplinesc următoarele roluri:
o blocul v reprezintă blocul funcțional de inițializare;
o blocul s descrie instrucțiunea/instrucțiunile ce urmează a fi executate dacă
condiția c este adevărată;
o blocul vr descrie actualizarea stărilor variabilelor programului cu rol deosebit
în evaluarea condiției (ciclu cu contor).
13
PC 1 – CURSUL 1 SILVESTRU CĂTĂLIN
Figura 1.4. structuri repetitive: a) ciclu cu test inițial, b) ciclu cu test final, c) ciclu cu contor
Cu ajutorul acestor 3 tipuri de structuri logice, putem crea scheme logice mai mult sau mai
puțin complexe, în funcție de problema dată și de algoritmul de rezolvare ales.
declarații de date – unde se fac precizări referitoare la tipul datelor de intrare utilizate în
algoritm, cât și modul de organizare a acestora;
instrucțiuni efective – în cadrul cărora se precizează operațiile ce se efectuează asupra
datelor de intrare, fiind astfel obținute valorile datelor de ieșire;
instrucțiunea stop – marchează sfârșitul logic al algoritmului;
instrucțiunea end – marchează sfârșitul fizic al algoritmului;
comentariile – unde sunt marcate destinația algoritmului, informații utile în procesul
proiectării algoritmului și, ulterior, utile pentru depanarea ori dezvoltarea aplicației.
14
PC 1 – CURSUL 1 SILVESTRU CĂTĂLIN
declarații de date;
instrucțiune 1;
instrucțiune 2;
...
Instrucțiune n;
stop;
end;
15
PC 1 – CURSUL 1 SILVESTRU CĂTĂLIN
c) Structuri repetitive.
16
PC 1 – CURSUL 1 SILVESTRU CĂTĂLIN
17
PC 1 – CURSUL 1 SILVESTRU CĂTĂLIN
Desigur că acest program nu va realiza nimic, fiind pur și simplu alcătuit din simpla definire a
funcției main().
Definirea oricărei funcții este alcătuită din antetul funcției și corpul funcției. Antetul funcției
reprezintă numele funcției, tipul rezultatului (datelor de ieșire) pe care îl calculează funcția și o listă de
parametrii încadrată între paranteze rotunde, prin care funcția comunică cu mediul exterior. Spre
exemplu:
tip_rezultat nume_funcție(listă_parametrii)
În cazul funcției main() prezentată mai sus, lista parametrilor este vidă, ir funcția nu oferă nici
un rezultat (lucru indicat prin specificarea tipului void). Rezultatul returnat de funcția main() este
preluat de sistemul de operare și de cele mai multe ori oferă informații despre modul de funcționare al
programului. Atunci când execuția unui program se termină cu succes, programul returnează uzual la
încheiere valoarea 0.
Un alt exemplu de program simplu în C/C++ care nu realizează nimic, dar care însă se termină
cu succes poate fi descris astfel:
int main(){
return 0;}
Instrucțiunea return (ce are formatul return expresie;)încheie execuția unei funcții și
returnează valoarea expresiei specificate în instrucțiune ca valoare a funcției. În cazul funcției main(),
aceasta se va încheia odată cu execuția programului.
Corpul funcției este încadrat între acolate (`{`corpul_funcției`}`), în cadrul căruia se vor
declara variabilele și constantele folosite, respectiv se vor scrie instrucțiunile necesar a fi executate în
cadrul funcției respective.
18