Sunteți pe pagina 1din 123

1. Fie un cerc de rază R, unde R este un număr real, citit de la tastatură.

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)

Varianta 2: aproximarea la 2 zecimale in momentul afișării


R = float(input("R="))
P = 2*math.pi*R
A = math.pi*R*R
print("Perimetrul =" '%.2f' %P)
print("Aria =" '%.2f' %A)

2. Se citesc de la tastatură două numere reale a și b. Să se interschimbe valorile variabilelor a și


b, apoi să se afișeze acestea.
a = float(input ("a="))
b = float(input("b="))
aux = a
a=b
b = aux
print("a =", a) print("b =", b)
3. Să se scrie un program care determină c.m.m.d.c. dintre două numere naturale, folosind
teorema împărțirii cu rest (algoritmul lui Euclid).
a = int(input ("a="))
b = int(input("b="))
d=a
i=b
r=i
while r>1:
r = d%i
if r == 0:

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)

8. Să se scrie un program ce citește de la tastatură un număr real x și afișează pe ecran valoarea


funcției: (3)

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.")

16. Fie x un număr real. Să se calculeze √3 x , folosind relatia de recurenta: (5)

Rezultatul trebuie să fie obținut cu o precizie mai bună decât 10-4 .


x=int(input("x="))
r1=1
r2=1
for i in range (2,x):
r2 =(2*r1+x/(r1*r1))/3
r1 = r2
i=i+1
print( '%.4f' %r2)

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))

31. Să se realizeze programul care efectuează înmulțirea dintre un vector şi o matrice.


import numpy as np
m=int(input("Numar de linii:"))
n=int(input("Numar de coloane:"))
print("Elemente matricii sunt:")
matrix=(list(map(int, input().split())))
matrix=np.array(matrix)
print("Elemetele vectorului sunt:")
vector = list(map(int, input().split()))
vector = np.array(vector)
result = np.array(vector)
print("Rezultatul este:")
print(result)
32. Fie o matrice pătratică de dimensiuni nxn. Să se sorteze descrescător elementele de pe
diagonala principală și cea secundară.
def input_matrix(n):
matrice = []
for i in range(n):
linie = input(f"Introduceti elementele liniei {i+1} separate printr-un spatiu: ").split()
matrice.append([int(x) for x in linie])
return matrice

def diagonale_sortate (matrice):


n = len(matrice)
for i in range(n):
for j in range(i+1,n):
if matrice[i][i] < matrice[j][j]:
matrice[i][i], matrice[j][j] = matrice[j][j], matrice[i][i]

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

n = int(input("Introduceti dimensiunea matricei patrate: "))


matrice = input_matrix(n)
matricea_sortata = diagonale_sortate(matrice)

for linie in matricea_sortata:


for elemente in linie:
print(elemente, end='')
print()

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 no_common_digits(a, b):


a_digits = set(str(a))
b_digits = set(str(b))
return not bool(a_digits & b_digits)

S = int(input("Introduceti valoarea lui S: "))


18
perechi = [(a, b) for a, b in itertools.combinations(range(1, S), 2) if a <= b and
no_common_digits(a, b) and a + b == S]

with open("perechi.txt", "w") as f:


for pereche in perechi:
f.write(f"{pereche[0]} {pereche[1]}\n")

print(f"{len(pairs)} pairs written to pairs.txt")


34. Se citește dintr-un fișier o secvență formată din maxim 100 de litere mici, până la
întâlnirea caracterului punct. Dacă există în secvența citită litere alăturate egale, aceste
litere vor fi eliminate. Dacă în urma eliminării se obțin din nou litere alăturate egale, se
elimină și acestea, eliminările efectuându-se până când în secvență nu există litere
alăturate egale.

def remove_adjacent_duplicates(string):
result = []
for char in string:
if len(result) == 0 or char != result[-1]:
result.append(char)
return "".join(result)

with open("file.txt", "r") as file:


string = file.readline().strip()
while string != ".":
string = remove_adjacent_duplicates(string)
print(string)
string = file.readline().strip()
break
35. 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 : 9*{x}*x+6*[x]-5.
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*x*pf-6*pi-5)

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

7.1. Definirea unei funcții

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.

O declarație de parametru formal specifică tipul și numele parametrului (tip nume).


Parametrii unei funcții constituie legătura dintre funcție și mediul exterior. Aceștia desemnează
date primite de funcție din exterior, date de care depind prelucrările efectuate de funcție.
Parametrii formali descriu în mod formal operațiile la care sunt supuse datele ce vor fi transmise
de programul apelant spre subprogram.
Pentru a returna un rezultat dintr-o funcție, se utilizează instrucțiunea return, ce are ca
format:
return expresie;

Aceasta evaluează expresia și încheie execuția funcției, returnând în exterior valoarea


expresiei. În cazul în care expresia este vidă, funcția nu returnează nici o valoare.
În concluzie, o funcție se încheie fie după executarea ultimei instrucțiuni (caz în care funcția
nu returnează nici o valoare), fie la întâlnirea instrucțiunii return.

2
PC 1 – CURSUL 7 SILVESTRU CĂTĂLIN

Exemple de definiții de funcții


Exemplul 1
void afisare(void)
{ printf(“Mesaj afisat\n”); }
/* o funcție definită fără parametrii, denumită afișare, ce are rolul de a
afișa un mesaj și de a nu returna nici un rezultat */

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

if, prin urmare funcția returnează valoarea parametrului y. Prin urmare,


funcția returnează minimul dintre cele două valori. */

7.2. Declararea funcțiilor


Definiția unei funcții informează compilatorul despre formatul funcției (tipul de rezultat, nume,
parametrii) și descrie prelucrările efectuate de funcție. Aceasta apare înaintea apelului funcției numai
în cazuri particulare; lucru ce nu este întotdeauna posibil (funcția nu este definită în același fișier sursă
ori este o funcție standard dintr-o bibliotecă a limbajului).
Declararea unei funcții informează compilatorul despre existența funcției și formatul acesteia.
Mai exact, o declarație de funcție specifică numele funcției, tipul rezultatului returnat de funcție și
parametrii acesteia. Declararea unei funcții se mai numește și prototip. Formatul unei declarații este:
tip nume(lista_parametrii);

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.

Exemple de declarații de funcții


Exemplul 1
long f1(int a, int v[10]);
/* funcția f1() conține doi parametrii (unul de tip int și un vector de 10
elemente de tip int) și întoarce ca rezultat o valoare de tip long int. În
cadrul acestei declarații este specificat atât numele parametrilor cât și
tipul acestora. */

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

7.3. Apelul funcțiilor


Apelul unei funcții poate fi realizat în două moduri:
- printr-o instrucțiune de apel;
- ca operand într-o expresie.

Formatul general al unui apel de funcție este:


nume(lista_parametrii_actuali);

 nume – reprezintă numele funcției.


 lista_parametrii_actuali – reprezintă o succesiune de expresii separate prin
virgulă. La apelul unei funcții, valorile acestor parametrii sunt atribuite, în ordine,
parametrilor formali corespunzători.

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.

Exemple de apelare de funcții


Exemplul 1
int a=5, b=10, m;
m=min(a, b);
/* cele 3 variabile au fost declarate, iar variabilei m a fost atribuit
minimul dintre a și b prin apelarea funcției min definită anterior. În acest
caz, valoarea parametrului actual a înlocuiește parametrul formal x (x va
lua valoarea 5), iar valoarea parametrului actual b înlocuiește parametrul
formal y (y va lua valoarea 10). */

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. */

7.4. Transferul parametrilor prin referință


În subcapitolul precedent sa observat că transferul parametrilor se face prin valoare în limbajul
C/C++. Frecvent apare necesitatea de a modifica valorile parametrilor funcției și de a utiliza în afara
funcției valorile modificate. Pentru acest lucru este necesară transmiterea adreselor parametrilor către
funcție.
Există două modalități de a transmite parametrii prin referință:
 prin utilizarea pointerilor – soluție utilizabilă atât în limbajul C, cât și C++;
 prin utilizarea referințelor – soluție utilizabilă numai în limbajul C++.

6
PC 1 – CURSUL 7 SILVESTRU CĂTĂLIN

7.4.1. Utilizarea pointerilor ca parametrii


Funcția schimb(), ce intenționa interschimbarea valorilor parametrilor x și y, va transmite de
această dată adresele parametrilor ce trebuie modificați:
void schimb(int *x, int *y)
{
int aux;
aux=*x; *x=*y; *y=aux;
printf(“x=%d\ty=%d\n”, *x, *y);
}
//se vor presupune aceleași valori:
int a=4, b=7;
//secvența de instrucțiuni:
schimb(&a, &b);
printf(“a=%d\tb=%d\n”, a, b);
/* se va afișa pe ecran:
x=7 y=4
a=7 b=4
interschimbarea fiind realizată cu succes */

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ță (&).

7.4.2. Utilizarea tipului referință


Tipul referință este o facilitate suplimentară a limbajului C++, ce are în vedere transmiterea
parametrilor prin referință.
O variabilă de tip referință conține adresa unei variabile (asemănător unui pointer), dar nu este
interpretată ca pointer, ci ca un alt nume asociat variabilei.
Pot fi declarate referințe către variabile de orice tip. În cadrul unui program pot fi utilizate atât
variabila, cât și referința către ea. Declararea unei referințe este de forma:
tip & variabila_referinta=variabila_referita;
/* declarația va crea un alt nume (variabila_referinta) pentru variabila
referita, având tipul tip */

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

Exemplu de declarare a unei referințe


int x=8;
int &rx=x;
cout<<x; cout<<rx; //ambele instrucțiuni vor afișa aceeași valoare

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 */

În concluzie, utilizarea referințelor drept parametrii are toate avantajele pointerilor


(posibilitatea de a modifica valorile parametrilor), fără complicații de sintaxă.
Orice operație de referențiere se poate realiza și cu ajutorul pointerilor, afirmația reciprocă
nefiind valabilă.

7.4.3. Exemple de funcții de citire/afișare a vectorilor prin intermediul transferului de


parametrii
De reamintit faptul că numele unui tablou este un pointer către primul element al tabloului! Prin
urmare, utilizarea vectorilor ca parametri oferă la definirea/declararea funcției posibilitatea utilizării
pentru declararea parametrului una din construcțiile echivalente:
tip * , tip[] ori tip[max]

8
PC 1 – CURSUL 7 SILVESTRU CĂTĂLIN

 tip reprezintă tipul componentelor vectorului;


 max reprezintă numărul maxim de elemente din vector (nu este obligatorie specificarea
numărului de elemente din vector între parantezele pătrate).
Apelarea unei funcții ce are parametrii de tip tablou, necesită transmiterea doar a numelui
tabloului (acesta fiind el însăși o adresă).
Se dă drept exemplu un program care citește un vector v cu n elemente întregi și un vector p cu
m elemente întregi, afișând apoi pe ecran cei doi vectori citiți. Exemplul va fi dat pentru varianta C prin
utilizarea pointerilor, precum și varianta C++ ce va utiliza referințe în loc de pointeri.

Varianta C
#include <stdio.h>

void cit_vector(int *, int *);


void afi_vector(int *, int);
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);
}

void cit_vector(int x[10], int *nr)


{
int i;
printf(“Nr. elemente= ”); scanf(“%d”, nr);
printf(“Introduceti elementele vectorului: “);
for(i=0; i<*nr; i++)
scanf(“%d”, &x[i]);
}

void afi_vector(int y[10], int nr)


{
int i;
printf(“Elementele vectorului sunt: “);

9
PC 1 – CURSUL 7 SILVESTRU CĂTĂLIN

for(i=0; i<nr; i++)


printf(“%d ”, y[i]);
printf(“\n”);
}

Î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>

void cit_vector(int *, int &);


void afi_vector(int *, int);

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);
}

void cit_vector(int x[10], int &nr)


{
cout<<”Nr. elemente= ”; cin>>nr;
cout<<”Introduceti elementele vectorului: “;
for(int i=0; i<nr; i++)
cin>>x[i];
}

10
PC 1 – CURSUL 7 SILVESTRU CĂTĂLIN

void afi_vector(int y[10], int nr)


{
cout<<”Elementele vectorului sunt: “;
for(int i=0; i<nr; i++)
cout<<y[i]<<” “;
cout<<endl;
}

7.5. Parametrii funcției main()


Până acum au fost descrise funcții main() fără parametri. Este necesar uneori ca sistemul de
operare să comunice programului anumite informații. Mai precis, la lansarea programului din sistemul
de operare în mod linie de comandă se pot transmite programului diferite opțiuni ori date inițiale, ca
argumente în linia de comandă. Argumentele din linia de comandă sunt succesiuni de caractere separate
prin spații sau caractere TAB.
Pentru funcția main() pot fi utilizați trei parametri:
main(int argc, char *argv[], char *env[]);
 argc – indică numărul de argumente din linia de comandă (argc>0);
 argv – reprezintă un vector în care sunt reținute în ordine cele argc argumente din linia
de comandă, ca șiruri de caractere (argv[0] este numele programului);
 env – reprezintă un vector în care sunt reținute informații de mediu ale sistemului de
operare (prompterul sistemului, căi implicite de căutare etc.), ultimul element al
vectorului fiind NULL, pentru a marca sfârșitul listei.

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

Programul în format cod din cardul fișierului radical.cpp este:


double R2(double a)
{
double x0=a, x1;
for(int i=1; i<=7; i++)
{
x1=0.5*(x0+a/x0);
x0=x1;
}
return x1;
}

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.

6.1.1. Declararea unui pointer de date


Formatul general de declarare a unui pointer este următorul:
tip * variabila_pointer;

Unde tip reprezintă tipul de bază al pointerului, ce indică tipul datelor memorate la adresa
conținută în variabila_pointer.

Exemple de declarare de pointeri


int * p; - p este un pointer cu tipul de bază int, ce va conține adresa unei zone de
memorie la care este memorat un număr întreg.
char * s; - s este un pointer cu tipul de bază char, ce va conține adresa unei zone de memorie la
care este memorat un caracter.
Declarațiile prezentate sunt asemănătoare cu declarația unei variabile uzuale, diferența fiind
prezența asteriscului ce indică faptul că variabila este de tip pointer.
Tipul de bază al unui pointer poate fi de orice tip al limbajului, inclusiv void. Un pointer de tip
void (void * ) se numește pointer generic.
Există și o constantă pointer specială, denumită NULL, ce nu conține adresa nici unei zone de
memorie, valoarea constantei fiind 0.
De asemenea, indicarea tipului de bază al pointerului este esențială! Adresa unei zone de
memorie este de fapt adresa primului octet din zona respectivă (o valoare numerică). Prin indicarea
tipului de bază al pointerului, se poate determina, în primul rând, dimensiunea zonei de memorie a cărei
adresă este memorată în pointer (de exemplu: 1 octet pentru char, 4 octeți pentru long int, etc.). Pentru
a afla valoarea memorată într-o anumită zonă de memorie este necesară, pe lângă dimensiunea zonei de
memorie, cunoașterea codului utilizat pentru reprezentarea datelor.

1
PC 1 – CURSUL 6 SILVESTRU CĂTĂLIN

6.1.2. Operații cu pointeri


1. Operația de referențiere
Reprezintă operația prin care se extrage adresa de memorie a unei variabile. Rezultatul acestui
tip de operații este un pointer, iar operația se realizează cu ajutorul operatorului unar & (numit
operatorul de referențiere ori operatorul adresă):
&variabila;

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

4. Adunarea/scăderea dintre un pointer și un număr întreg


Rezultatul în urma operației de adunare/scădere dintre un pointer și un număr întreg (p+n / p-
n) este mărirea/micșorarea adresei de memorie în pointer cu n*sizeof(tip).

5. Scăderea dintre doi pointeri


Scăderea dintre doi pointeri poate fi posibilă numai în cazul în care cei doi pointeri au același
tip de bază. Rezultatul este o valoare întreagă ce reprezintă diferența dintre adrese, raportată la
sizeof(tip).

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) ...

7. Afișare unui pointer


Afișarea valorii unui pointer în limbajul C poate fi efectuată cu ajutorul funcției printf(),
precizând specificatorul de format %p:
int a, *q=&a;
printf(“%p”, q); //pe ecran se va afișa adresa variabilei a, în baza
16

6.1.3. Legătura dintre pointeri și tablouri


Numele unui tablou este un pointer constant ce are ca valoare adresa primului element din
tablou.
Se consideră următoarea declarație:
tip v[LgMax];

Operațiile cu pointeri reprezintă o alternativă pentru adresarea elementelor unui tablou.


Expresiile următoare sunt echivalente cu:
v &v[0] //adresa primului element din tablou
v+i &v[i] //adresa elementului de pe pozitia i din tablou
*v v[0] ori &v[0][0] //primul element din tablou
*(v+i) v[i] ori &v[i][0] //elementul de pe pozitia i din tablou
*(*(v+i)+j) v[i][j] //elementul de pe linia i si coloana j
v++ v[1] //elementul de pe pozitia 1

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;

Utilizarea funcțiilor standard de lucru cu șiruri de caractere


Header-ul string.h conține declarații ale unor funcții de lucru cu șiruri de caractere ce pot fi
foarte folositoare în rezolvarea eficientă a unor probleme ce implică șiruri de caractere.

Determinarea lungimii efective a unui șir de caractere


O variantă și mai simplificată față de exemplu anterior de determinare a lungimii efective a
unui șir de caractere, implică utilizarea funcției strlen()., funcție ce returnează la apelare lungimea
șirului. Prototipul funcției este:
unsigned strlen(const char *s);

Copierea unui șir de caractere


Copierea unui șir de caractere într-un altul poate fi realizată cu ajutorul funcției strcpy(). Apelul
strcpy(destinație, sursa) copiază caracter cu caracter șirul sursă în șirul destinație, până după copierea
caracterului NULL și returnează adresa de început a șirului destinație. Spațiul de memorie alocat șirului
destinatie trebuie să fie suficient de mare pentru a memora caracterele șirului sursa. Prototipul funcției
este:
char * strcpy(char *destinatie, const char *sursa);

Copierea unui prefix al unui șir de caractere


Copierea primelor n caractere ale unui șir sursă într-un șir destinație se realizează cu ajutorul
funcției strncpy(). Asemănător funcției de copiere integrală, apelul strncpy(destinatie, sursa, n) are ca
efect copierea primelor n caractere din șirul sursă în șirul destinație și returnează adresa de început a
șirului destinație. Dacă lungimea șirului sursă este mai mică decât n, se copiază întreg șirul sursă în
șirul destinație, încheiat prin plasarea caracterului NULL. Dacă lungimea șirului sursă este mai mare
sau egală cu n, atunci la sfârșitul șirului destinație nu va fi plasat caracterul NULL. Protoripul funcției
este:

4
PC 1 – CURSUL 6 SILVESTRU CĂTĂLIN

char * strncpy(char *destinatie, char *sursa, unsigned n);

Concatenarea a două șiruri de caractere


Operația de concatenare a două șiruri poate fi realizată cu funcția strcat(). Apelul
strcat(destinatie, sursa) copiază caracterele șirului sursă la sfârșitul șirului destinație și returnează adresa
de început a șirului obținut după concatenare (destinație). Zona de memorie alocată șirului destinație
trebuie să fie suficient de mare pentru a reține rezultatul concatenării. Prototipul funcției este:
char * strcat(char *destinatie, const char *sursa);

Concatenarea unui șir de caractere cu un prefix al altui șir de caractere


Concatenarea primelor n caractere dintr-un șir sursă la șirul destinație se realizează cu funcția
strncat(). Apelul strncat(destinatie, sursa, n) are ca efect copierea primelor n caractere din șirul sursă și
plasarea acestora la sfârșitul șirului destinație, returnând apoi adresa de început a șirului destinație. Dacă
lungimea șirului sursă este mai mică decât n, atunci va fi concatenat întreg șirul sursă la șirul destinație.
La sfârșitul șirului rezultat se plasează caracterul NULL. Prototipul funcției este:
char * strncat(char *destinatie, char *sursa, unsigned n);

Compararea a două șiruri de caractere


Compararea lexicografică a două șiruri de caractere poate fi realizată ușor cu funcția strcmp().
Apelul strcmp(sir1, sir2) compară din punct de vedere lexicografic cele două șiruri de caractere și
returnează o valoare:
- <0 pentru s1<s2 (în ordine lexicografică, ordinea cuvintelor din dicționar);
- =0 pentru s1==s2;
- >0 pentru s1>s2 (în ordine lexicografică).
Prototipul funcției este:
int strcmp(const char *sir1, const char *sir2);

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().

Căutarea primei apariții a unui caracter într-un șir


Pentru a determina prima apariție a unui caracter într-un șir se poate utiliza funcție strchr().
Apelul strchr(sir, caracter) caută prima apariție a caracterului în șir și returnează un pointer care are ca
valoare adresa primului caracter căutat din șir (în cazul în care există) sau NULL (în cazul în care nu
există). Prototipul funcției este:
char * strchr(const char*sir, int caracter);

5
PC 1 – CURSUL 6 SILVESTRU CĂTĂLIN

Căutarea primei apariții a unui șir într-un alt șir


Pentru a determina dacă un șir este subșir altui șir se poate utiliza funcția strstr(). Apelul
strstr(sir1, sir2) determină prima apariție a șirului sir2 în șirul sir1 și returnează un pointer către
începutul primei apariții al lui sir2 în sir1, ori NULL în cazul în care nu apare. Prototipul funcției este:
char * strstr(const char *sir1, const char *sir2);

Transformări între majuscule și minuscule


Există posibilitatea de a transforma toate literele mari dintr-un șir în litere mici și viceversa,
prin apelarea funcțiilor strlwr() și strupr(). Apelarea funcției strlwr(sir) transforma literele mari din șir
în litere mici, iar apelarea funcției strupr(sir) transformă literele mici din șir în litere mari. Prototipul
funcțiilor este:
char *strlwr(char *sir);
char *strupr(char *sir);

Separarea unităților lexicale dintr-un șir de caractere


O unitate lexicală este o secvență de caractere dintr-un șir, delimitată de separatori. Funcția
strtok() permite extragerea succesivă a unităților lexicale dintr-un șir de caractere. Apelarea acestei
funcții este specială, deoarece prima dată este apelată sub forma;
strtok(sir, separatori);

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

Șirul de caractere sir va conține reprezentarea numărului nr în baza de numerație baza


(2≤baza≤36).
În header-ul stdlib.h sunt de asemenea prezente funcții ce realizează conversia în sens invers
(din șir de caractere în valoare numerică):
atoi();
atol();
atof();
strtod();
_strtold();
strtol();
strtoul();

6.2. Tipul de date struct


Tablourile sunt structuri de date de același tip. Cu toate acestea, necesitatea utilizării structurilor
de date care să conțină date de tipuri diferite apare destul de frecvent. O structură de acest fel este
denumită articol (ori înregistrare sau structură) și este implementată în limbajul C/C++ cu ajutorul
tipului de date struct.
Datele înglobate într-un articol sunt denumite câmpuri ori membri. Aceste câmpuri nu mai sunt
obligatoriu de același tip și nu mai pot fi referite prin intermediul indicilor. De aceea, fiecare câmp are
un nume distinct, ce este utilizat pentru a face referire la câmpul respectiv.

6.2.1. Declararea unui tip struct


Declararea unei structuri are următoarea sintaxă:
struct [nume_tip]
{lista_declaratii_campuri} lista_variabile;

Astfel se declară o structură cu numele nume_tip ce conține câmpurile declarate în


lista_declaratii_campuri și o listă de variabile ce au tipul struct definit.

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;

Exemple de declarări de structuri


1. O structură denumită punct ce reprezintă un punct de coordonate x și y în planul cartezian:
struct punct
{double x, y;};

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];

La declararea acestei structuri a fost declarată și variabila clasa ca un vector cu 30 de


componente de tip elev.

6.2.2. Referirea la un câmp al unei structuri


Acest lucru se realizează prin specificarea numelui structurii și a numelui câmpului respectiv,
separate prin operatorul de selecție directă ‘.’:
variabila_struct.camp;

Pe baza exemplelor prezentate anterior la declarare, se poate face referire la:


 ordonata punctului P1
P1.y;

8
PC 1 – CURSUL 6 SILVESTRU CĂTĂLIN

 numele primului elev din vectorul clasa:


clasa[0].nume;
 media generală a celui de al 10-lea elev din vectorul clasa:
clasa[10].MG;

6.2.3. Inițializarea unei structuri


Structura a fost declarată însă nu și inițializată. Asemenea oricărei variabile, inițializarea unei
variabile de tip struct poate fi realizată încă de la declarare. La inițializare, trebuie precizate în ordine
valorile ce vor fi atribuite câmpurilor structurii și trebuie ca valorile respective să coincidă tipului
câmpului. Valorile sunt separate prin virgulă și încadrate între acolade, iar numărul de valori precizate
la inițializare poate fi mai mic sau egal cu numărul de câmpuri ale structurii (caz în care pot fi inițializate
doar primele câmpuri ori toate).
Spre exemplu, declararea unei variabile de tip struct data, inițializată cu data de 28/01/2018:
struct data
{int zi, an;
char luna[10];};
struct data zi_nastere={29, 2018, “Ianuarie”};

6.2.4. Operații cu structuri


Pentru a putea realiza operația de citire/afișare a unei structuri, este necesară citirea/afișarea
separată a fiecărui câmp al structurii.
Singura operație ce se poate realiza la nivel de structură este atribuirea. Mai explicit, o variabilă
de tip struct poate fi atribuită unei alte variabile de tip struct. Spre exemplu, o variabila de tip struct data
denumită azi poate fi inițializată cu valoarea variabilei zi_nastere (declarată în exemplul anterior):
struct data azi;
azi=zi_nastere;

6.3. Asocierea unui nume pentru un tip de date


Tipurile de date predefinite ale limbajului (int, char, float, unsigned etc.) se identifică printr-un
nume specific. În secțiunea de declarare a unei structuri, a fost prezentat faptul că atunci când este
definit un tip struct îi poate fi asociat un nume.
Unui tip de date îi poate fi asociat un nume, indiferent dacă acesta este un tip deja predefinit ori
definit de către un programator, cu ajutorul construcției typedef:
typedef descriere_tip nume_tip;

9
PC 1 – CURSUL 6 SILVESTRU CĂTĂLIN

Exemple de asocieri de nume pentru tipuri de date


typedef float real
//asocierea tipului predefinit float un nume mai sugestiv – real

typedef int *pint


//asocierea pointerului cu tipul de bază int numele pint

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:

#define dim_max 50 //lungimea maximă a stivei


typedef int stiva[dim_max]; //tipul stiva implementat ca vector
stiva S; //stiva
int vf; //variabila de varf a stivei

Crearea unei stive vide


Pentru a crea o stivă vidă trebuie inițializat vârful acesteia cu valoarea -1 (vârful stivei indică
poziția ultimului element introdus în stivă, iar elementele sunt memorate în vector începând cu poziția
0):
vf=-1;

Inserarea unui element în stivă


Inserarea unui element x în stiva S cere mai întâi verificarea stivei, mai exact, dacă mai este loc
în stivă pentru a introduce elementul x. Dacă stiva este plină, inserarea nu poate fi realizată deoarece se
depășește mărimea stivei. Dacă mai există loc, atunci inserarea elementului x în stivă poate fi realizată.
if(vf==dim_max-1)
printf(“Stiva este plina!\n”);
else
S[++vf]=x;

Extragerea unui element din stivă


Extragerea unui element din stivă implică de asemenea o verificare; aceea de a vedea dacă în
stivă sunt prezente elemente sau nu (stivă vidă). Dacă da, se reține elementul de la vârful stivei într-o
variabilă (de pildă x), după care se micșorează cu o unitate vârful stivei.
if(vf<0)
printf(“Stiva este goala!\n”);
else
x=S[vf--];

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.

Prin urmare, singurele operații ce pot fi executate cu o coadă sunt:


 crearea unei cozi vide;
 inserarea unui element;
 extragerea unui element;
 accesarea unui element.

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

typedef int coada[dim_max]; //tipul coada implementat ca vector


coada C; //coada
int inc, sf; //variabilele de inceput si sfarsit ale cozii

Crearea unei cozi vide


Pentru a crea o coadă vidă trebuie inițializate variabilele inc și sf cu valoarea 0 respectiv -1:
inc=0;
sf=-1;

Inserarea unui element în coadă


Inserarea unui element x în coada C cere mai întâi verificarea cozii, mai exact, dacă mai este
loc în coadă pentru a introduce elementul x. Dacă coada este plină, inserarea nu poate fi realizată
deoarece se depășește mărimea cozii. Dacă mai există loc, atunci inserarea elementului x în coadă poate
fi realizată.
if(sf==dim_max-1)
printf(“Coada este plina!\n”);
else
C[++sf]=x;

Extragerea unui element din coadă


Extragerea unui element din coadă implică de asemenea o verificare; aceea de a vedea dacă în
coadă sunt prezente elemente sau nu (coadă vidă). Dacă da, se reține elementul de la începutul cozii
într-o variabilă (de pildă x), după care se mărește cu o unitate începutul cozii.
if(inc>sf)
printf(“Coada este goala!\n”);
else
x=C[inc++];

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.

5.1. Tablouri de date


Prin tablou de date se înțelege un ansamblu de date de același tip, memorate într-o zonă de
memorie adiacentă sub un nume comun (numele de identificare al tabloului).
O variabilă de tip tablou se declară astfel:
tip nume[nr_elem];

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:

nume[0] nume[1] nume[2] … nume[nr_elem-2] nume[nr_elem-1]

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

Se poate de asemenea inițializa elementele unei variabile tablou încă de la declarare:


tip nume[nre]={val_0, val_1, ..., val_i}; //i<=nre

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

În memorie, tabloul arată astfel:


10 15 20 4
t[0] t[1] t[2] t[3]

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 */

În memorie, tabloul arată astfel:


m Coloana 0 Coloana 1 Coloana 2
Linia 0 1 2 3
Linia 1 4 5 6

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 exemplu, pentru a ne referi la elementele din tabloul exemplificat anterior:


m Coloana 0 Coloana 1 Coloana 2
Linia 0 m[0][0] m[0][1] m[0][2]
Linia 1 m[1][0] m[1][1] m[1][2]

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

5.2. Prelucrarea vectorilor


5.2.1. Citirea vectorilor
Citirea unui vector este definită prin citirea elementelor vectorului, unul câte unul. Spre
exemplu, considerăm citirea unui vector v cu n elemente (n≤100) de tip unsigned:
unsigned v[100], n, i;
printf(“n=”); scanf(“%u”, &n);
for(i=0; i<n; i++)
{
printf(“v[%u]=”, i);
scanf(“%u”, &v[i]);
}

5.2.2. Afișarea unui vector


Afișarea unui vector este definită prin scrierea elementelor vectorului, unul câte unul. Spre
exemplu, afișarea vectorului citit în exemplul anterior:
for(i=0; i<n; i++)
printf(“%u”, v[i]);

5.2.3. Copierea unui vector


Copierea vectorului v într-un alt vector, de pildă w, nu se poate realiza printr-o atribuire de
forma w=v. Copierea trebuie realizată element cu element:
for(i=0; i<n; i++)
w[i]=v[i];

5.2.4. Inversarea ordinii elementelor unui vector


Inversarea ordinii elementelor dintr-un vector implică interschimbarea primului element cu
ultimul, al doilea cu penultimul ș.a.m.d. Prin urmare, vectorul este parcurs doar pentru jumătate din
lungimea lui. Pentru această operație, este nevoie de o variabilă ajutătoare auxiliară ce va memora
temporar variabilele interschimbate, pentru a nu suprascrie elementele din vector:
for(i=0; i<n/2; i++)
{
aux=v[i];
v[i]=v[n-i-1];
v[n-i-1]=aux;
}

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].

5.2.5. Determinarea elementului minim/maxim al unui vector


Pentru a determina cel mai mic element din vector, respectiv cel mai mare, se vor considera
două variabile (min și max) în care se vor reține la fiecare pas minimul și maximul dintre elementele
analizate. Se începe prin inițializarea variabilelor cu primul element din vector (v[0]). Se parcurge apoi
vectorul, realizând comparări succesive cu fiecare element din vector cu min/max și actualizând
eventual variabilele după comparare:
min=v[0];
max=v[0];
for(i=1; i<n; i++)
{
if(min>v[i])
min=v[i];
if(max<v[i])
max=v[i];
}

5.2.6. Verificarea unei proprietăți


În lucrul cu vectorii, poate fi necesar a se verifica dacă toate elementele unui vector au o anumită
proprietate P ori dacă există în vector un element care are proprietatea P. O variantă generală pentru
rezolvarea unei astfel de probleme implică considerarea instrucțiunii P(x) ca având valoarea 1 dacă x
îndeplinește proprietatea respectivă, și 0, în caz contrar.

Exemplu de verificare a unei proprietăți


Pentru a verifica dacă toate elementele unui vector au proprietatea P, trebuie verificat succesiv
fiecare element în parte al vectorului. Poate fi utilizată o variabilă de tip „semafor” ce reține rezultatul
verificării. Aceasta va avea valoarea 1 dacă toate elementele sunt conform proprietății P, ori 0 în caz
contrar. Se poate pleca de o ipoteză optimistă – „prezumția de nevinovăție” – prin inițializarea variabilei
semafor (de pilda ’ok’) cu valoarea 1. Prin parcurgerea vectorului, la găsirea unui element neconform,
variabila semafor devine 0:
for(ok=1, i=0; i<n && ok; i++) /* vectorul este parcurs integral ori
până la găsirea unui element neconform */
if(!P(v[i]))
ok=0;

4
PC 1 – CURSUL 5 SILVESTRU CĂTĂLIN

5.2.7. Căutarea unui element în vector


Una dintre cele mai frecvente aplicații cu vectori implică identificarea unui element într-un
vector. Vectorul poate fi ordonat sau nu, situație în care poate fi necesară parcurgerea integrală a
vectorului. O astfel de metodă poartă numele de „căutare secvențială”. în care sunt testate succesiv
elementele vectorului, fiind realizate n comparații. Spre exemplu, să presupunem vectorul v ce conține
n componente întregi (n≤100), printre care se dorește căutarea unei valori întregi x. Se verifică astfel
dacă printre elementele vectorului apare sau nu valoarea x:
for(gasit=i=0; i<n &&!gasit; i++)
if(v[i]==x)
gasit=1;

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

5.2.8. Sortarea unui vector


Problema ordonării unor elemente este frecvent întâlnită în lucrul cu tablouri. Au apărut astfel
numeroși algoritmi de sortare pentru a rezolva această problemă, fiecare având o anumită eficiență.
Printre cei mai simplii algoritmi de acest fel se numără: sortarea prin selecție, bubblesort, sortarea prin
inserție și sortarea prin numărarea aparițiilor. În continuare vor fi prezentate toate aceste soluții
enumerate.
Pentru a se putea face o exemplificare corectă, se va considera un vector v cu n elemente,
alcătuit din elemente e0, e1, ..., en-1. Cu ajutorul algoritmilor enumerați, se cere să se ordoneze crescător
elementele în vector.

Sortarea prin selecție


Această metodă de sortare are două variante: sortarea prin selecția elementului minim ori
sortarea prin selecția elementului maxim. Ambele variante au aceeași idee de bază: se selectează cel
mai mic element din vector (sau cel mai mare) și se plasează pe prima poziție în vector (respectiv, pe
ultima poziție). Se preia apoi cel mai mic element dintre elementele rămase și se plasează pe a doua
poziție în vector (ori cel mai mare dintre elementele rămase și se plasează pe penultima poziție) ș.a.m.d.,
proces ce se repetă de n-1 ori.
for(st=0; st<n-1; st++)
{
for(min=v[st], pozmin=st, i=st+1; i<n; i++)
if(v[i]<min)
{
min=v[i];
pozmin=i;
}
v[pozmin]=v[st];
v[st]=min;
}

Sortare prin bubblesort


Metoda de sortare bubblesort implică parcurgerea vectorului și realizarea comparațiilor
succesive a fiecare două elemente vecine și (dacă este cazul) interschimbându-le. Sortarea integrală a
vectorului nu poate fi realizată dintr-o singură parcurgere a vectorului. De aceea, procedeul se repetă
până când vectorul este sortat plus o ultimă trece în cadrul căreia nu se efectuează nici o interschimbare
(u ultimă verificare a vectorului).

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);

Sortare prin inserție


O metodă simplă de sortare, utilizată de pildă în viața reală atunci când ordonăm cărțile de joc
– se preia o carte neordonată și se plasează în poziția corectă în pachet până când pachetul devine
ordonat. Aceeași idee este aplicată și vectorului: se parcurge vectorul element cu element, iar la fiecare
pas i se caută poziția corectă a elementului curent v[i], până când se ajunge la ordonarea completă a
vectorului.
for(i=1; i<n; i++)
{
a=v[i];
for(poz=i; poz && v[poz-1]>a; poz--)
v[poz]=v[poz-1];
v[poz]=a;
}

Sortare prin numărarea aparițiilor


În cazul în care problema conține un vector al cărui elemente sunt definite ca numere naturale
dintr-un interval [0, m) de dimensiune redusă (un vector cu maxim m componente întregi), cea mai
bună metodă este sortarea prin numărarea aparițiilor. Vectorul este parcurs iar elementele sunt numărate
de câte ori apar în vector.

7
PC 1 – CURSUL 5 SILVESTRU CĂTĂLIN

#include <stdio.h>
#define m 1000
#define nrmax 10000

int n, v[m], s[nrmax];


int main(){
int x, i, j, nr;
printf(“n=”); scanf(“%d”, &n);
for(i=0; i<n; i++)
{
scanf(“%d”, &x);
v[x]++;
}
for(nr=i=0; i<m; i++)
for(j=0; j<v[i]; j++)
s[nr++]=i;
for(i=0; i<n; i++)
printf(“%d ”, s[i]);
}

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

for(i=j=k=0; i<n && j<m; )


if(a[i]<b[j])
c[k++]=a[i++];
else
c[k++]=b[j++];
for( ; i<n; i++)
c[k++]=a[i];
for( ; j<m; j++)
c[k++]=b[j];

5.3. Prelucrarea matricelor


5.3.1. Citirea matricelor
Citirea matricelor este realizată prin citirea elementelor acesteia linie cu linie, asemenea citirii
mai multor vectori simultan. Se presupune, de exemplu, o matrice M cu l linii și c coloane (l, c≤100),
cu elemente de tip int:
int M[100][100], l, c, i, j;
printf(“l=”); scanf(“%d”, &l);
printf(“c=”); scanf(“%d”, &c);
for(i=0; i<l; i++)
for(j=0; j<c; j++)
{
printf(“M[%d][%d]=”, i, j);
scanf(“%d”, &M[i][j]);
}

5.3.2. Afișarea unei matrice


Afișarea unei matrice se realizează afișând elementele acesteia, linie cu linie. similar citirii:
for(i=0; i<l; i++)
{
for(j=0; j<c; j++)
printf(“M[%d][%d]=%d ”, i, j, M[i][j]);
printf(“\n”);
}

9
PC 1 – CURSUL 5 SILVESTRU CĂTĂLIN

5.3.3. Prelucrarea matricelor pătratice


O matrice este pătratică dacă numărul de linii este egal cu numărul de coloane. Datorită acestei
particularități, pot fi realizate prelucrări specifice ce implică diagonalele matricei.

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]);

5.4. Șiruri de caractere


Un șir de caractere este reprezentat ca fiind o succesiune de caractere terminată cu caracterul
NULL (valoarea 0 în codul ASCII). Mai exact, un șir de caractere poate fi definit ca un vector de
lungime Ls al cărui elemente sunt de tip char. Declararea unei variabile de acest tip poate fi realizat
astfel:
char nume[Ls];

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’).

5.4.1. Citirea unui șir de caractere în limbajul C++


Pentru început, se presupune că a fost declarat un șir s de maxim 100 de caractere:
char s[100];
Citirea șirului se poate realiza prin utilizarea operatorului de citire:
cin>>s;

Î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.

5.4.2. Afișarea unui șir de caractere în C++


Afișarea șirului se poate realiza prin utilizarea operatorului de scriere:
cout<<s;

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).

5.4.3. Citirea unui șir de caractere în limbajul C


În limbajul C, un șir de caractere poate fi citit cu ajutorul funcției scanf(), prin utilizarea
specificatorului de format %s:
scanf(“%s”, s);

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);

Funcția citește în șirul s caracterele introduse de la tastatură, până la întâlnirea caracterului de


sfârșit de linie (\n), ce este convertit automat în caracterul NULL.

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);

5.4.4. Afișarea unui șir de caractere în limbajul C


Afișarea unui șir de caractere se realizează cu ajutorul funcției printf(), prin utilizarea
specificatorului de format %s:
printf(“%s”, 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ă.

4.1. Fișiere text în limbajul C++

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

Sintaxa pentru declararea unui stream de intrare și/sau ieșire este:


ifstream nume_stream;
ofstream nume_stream;
fstream nume_stream;

Unde nume_stream are același efect ca o variabilă.

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.

Exemple de deschidere a fișierelor


Exemplul 1:
ifstream f1;
f1.open(“info.in”);
ori
ifstream f1(“info.in”);

Î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.

Citirea datelor dintr-un fișier


După ce fișierul de intrare a fost deschis, pot fi realizate operațiile de citire. Pentru acest lucru
se pot utiliza fie operatorul de citire ’>>’, fie prin utilizarea unor funcții-membru specifice.
O particularitate a citirii cu ajutorul operatorului ’>>’ este faptul că acesta ignoră caracterele
albe. Dacă însă se dorește citirea tuturor caracterelor (incluzând spațiu, tab și enter), atunci acest mod
este inadecvat.
Prin urmare, poate fi utilizată funcția-membru get(). Aceasta poate fi folosită spre exemplu
astfel:
ifstream f1(“info.in”);
char c;
f1.get(c);

Î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

Scrierea datelor într-un fișier


După deschiderea fișierului de ieșire, pot fi realizate operații de scriere în cadrul acestuia prin
utilizarea operatorului de scriere ’<<’. Spre exemplu:
ofstream f2(“info.out”);
f2<<”Hello”;

Detectarea sfârșitului de fișier


Poate fi necesar în cadrul anumitor aplicații citirea tuturor datelor existente dintr-un fișier, fără
a avea în prealabil informații despre numărul acestora.
La deschiderea unui fișier este creat un pointer de fișier ce indică poziția curentă în stream.
Orice operație de citire deplasează pointerul de citire în stream-ul de intrare, respectiv, orice operație
de scriere deplasează pointerul de scriere în stream-ul de ieșire.
Pentru a afla momentul în care pointerul a ajuns la sfârșitul fișierului, este utilizată funcția
eof(). 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.
Exemplu de program pentru citirea și numărarea integrală a caracterelor existente într-un fișier:
ifstream f(“date.in”);
char c;
int nr=0;
while (!f.eof())
{
f.get(c);
nr++;
}
cout<<”Fisierul contine ”<<nr<<” caractere”;

Închiderea unui fișier


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 close(), astfel:
f.close();

4
PC 1 – CURSUL 4 SILVESTRU CĂTĂLIN

4.2. Fișiere text în limbajul C

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);

Nume este o constantă de tip șir de caractere ce reprezintă numele/specificatorul fișierului pe


suportul memoriei, iar mod este o constantă de tip șir de caractere ce indică modul în care vor fi accesate
datele din fișier.

Exemple de deschidere a fișierelor


FILE *f1, *f2, *f3;

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;
}

Citirea datelor dintr-un fișier


Fișierul de intrare a fost deschis, prin urmare, pot fi realizate operații de citire asupra lui. În
acest scop se utilizează funcția fscanf(). Formatul acestei funcții este următorul:
int fscanf(variabila_fisier, format, adr_var_1, ..., adr_var_n);

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.

Scrierea datelor într-un fișier


După ce fișierul a fost deschis ca fișier de ieșire, pot fi realizate operațiile de scriere prin
utilizarea funcției fprintf(). Formatul acestei funcții este următorul:
int fprintf(variabila_fisier, format, expresie_1, ..., expresie_n);

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

Detectarea sfârșitului de fișier


Asemenea observațiilor precizate anterior în subcapitolul de fișiere text în limbajul C++, poate
fi necesar în cadrul anumitor aplicații citirea tuturor datelor existente dintr-un fișier, fără a avea în
prealabil informații despre numărul acestora.
Pentru a afla momentul în care pointerul a ajuns la sfârșitul fișierului, este utilizată funcția
getc(). Aceasta va citi un singur caracter din fișierul de intrare și îl va returna ca un număr întreg.
Dacă această operație eșuează, va returna EOF (end-of-file).
Exemplu de program pentru citirea și numărarea integrală a caracterelor existente într-un fișier:
FILE *f = fopen(“date.in”, “r”);
int nr=0;
int c=getc(f);
while (c != EOF)
{
c=getc(f);
nr++;
}
printf(“Fisierul contine %d caractere”, nr);

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);

Exemple de aplicații cu citirea/scrierea datelor în fișiere


Exemplul 1 - program de copiere a unui fișier:
#include <stdio.h>
int main()
{
FILE *f1, *f2;
char x;
if(!(f1=fopen(“original.txt”, “r”)))
{
printf(“Eroare la deschiderea fisierului de intrare”);

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;
}

Exemplul 2 – un program ce citește un fișier de intrare cu n numere naturale (0<n≤100) și afișează


într-un alt fișier numerele citite descompuse pe factori primi, pe fiecare linie fiind scris câte o pereche
de forma (d, p), ce indică divizorul și puterea acestuia.
#include <stdio.h>
int main()
{
int n, i, d, p, er;
unsigned x;
FILE *f1 = fopen(“numere.in”, “r”);
if(!f1)
{
printf(“Eroare la deschiderea fisierului numere.in\n”);
return 1;
}
FILE *f2 = fopen(“numere.out”, “w”);
er = fscanf(f1, “%d”, &n);
if(!er || n>100 || n<0)
{
printf(“Numarul de valori este incorrect\n”);
return 2;

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 ’;’!

3.1. Instrucțiuni simple


În cadrul instrucțiunilor simple întâlnim două astfel de tipuri de instrucțiuni:

 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.

3.2. Instrucțiunea compusă


Acest tip de instrucțiune este reprezentat de o succesiune de instrucțiuni și declarații, cuprinse
între o pereche de acolade. Utilizarea instrucțiunilor compuse este necesară atunci când sintaxa permite
executarea unei singure instrucțiuni, fiind necesară totuși efectuarea mai multor operații în acel moment.
Această situație este întâlnită în cadrul structurilor alternative și repetitive.
Este foarte important de notat faptul că, declarațiile de variabile plasate înaintea instrucțiunilor
compuse permit utilizarea acelor variabile în cadrul acestor instrucțiuni. Însă, declarațiile care apar într-
o instrucțiune compusă sunt locale instrucțiunii, acestea fiind valabile numai în corpul instrucțiunii
compuse, din momentul declarării lor până la sfârșitul instrucțiunii. Putem face o asemănare cu
declarațiile globale și cele locale. Declarațiile globale erau acele declarații care apăreau înaintea unei
funcții (de exemplu main()) și care puteau fi folosite în cadrul oricăror funcții apelate în continuarea
acestora. Declarațiile locale (de pildă cele de la începutul funcției main()) pot fi folosite doar în cadrul
acelei funcții.

2
PC 1 – CURSUL 3 SILVESTRU CĂTĂLIN

Forma generală a unei instrucțiuni compuse este:


{
declarații;
instrucțiune_1;
instrucțiune_2;
...
instrucțiune_n;
}

3.3. Instrucțiuni structurate


3.3.1. Instrucțiunea if (structura alternativă simplă)
Această instrucțiune permite realizarea unei ramificări logice binare, în funcție de rezultatul
logic al unei expresii. Forma generală a unei instrucțiuni if este:
if (expresie)
instrucțiune_1;
else
instrucțiune_2;

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.

Exemplu instrucțiune if:


max=0;
if (a>b)
max=a;
ori
max=(a>b) ? a:b;

3.3.2. Instrucțiunea switch (structura alternativă multiplă)


Permite alegerea unei instrucțiuni dintr-un grup (listă), în funcție de valorile pe care le poate
lua expresia. Instrucțiunea switch este o generalizare a instrucțiunii if, diferența fiind faptul că aceasta
permite selectarea unei alternative din maximum n+1 posibile, pe când if permite maxim două
alternative. O altă diferență este faptul că în if se execută instrucțiunea corespunzătoare valorii
expresiei și atât, pe când switch execută și toate secvențele de instrucțiuni ale alternativelor case
următoare. Forma generală a unei instrucțiuni switch este:
switch (expresie)
{
case expresie-constantă_1: secvență-instrucțiuni_1;
case expresie-constantă_1: secvență-instrucțiuni_2;
...
case expresie-constantă_n: secvență-instrucțiuni_n;
default: secvență-instrucțiuni_n+1;
}

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).

Exemplu instrucțiune switch


switch (c)
{
case ‘+’: a+=b; break;
case ‘-’: a-=b; break;
case ‘*’: a*=b; break;
case ‘/’: a/=b; break;
case ‘~’:
case ‘!’: cout<<”Nu este operator binar!”; break;
default: cout<<”Eroare!”;
}

În funcție de valoarea variabilei de tip char c, se va efectua operația corespunzătoare între


variabilele a și b. Dacă variabila c are valoarea ’~’ sau ’!’, se va afișa mesajul corespunzător, iar dacă
c are orice altă valoare, se va afișa mesajul de eroare. Se observă că putem executa aceeași instrucțiune
pentru mai multe valori, valorile respective fiind înscrise pe ramuri succesive, asociind instrucțiunea
dorită ultimei ramuri (cazul ’~’ și ’!’).

5
PC 1 – CURSUL 3 SILVESTRU CĂTĂLIN

3.3.3. Instrucțiunea while (structură repetitivă condiționată anterior)


Instrucțiunea se execută repetat cât timp valoarea expresiei este nenulă. Pentru a evita apariția
unui ciclu infinit, este obligatoriu ca instrucțiunea care se execută să modifice cel puțin una dintre
variabilele care intervin în expresie, astfel încât aceasta să poată lua valoarea 0, ori să conțină o operație
de ieșire necondiționată din ciclu (exemplu: break;).
Dacă expresia are încă de la început valoarea 0, atunci instrucțiunea nu va fi executată nici
măcar o dată. De asemenea, instrucțiunea ce se va executa poate fi de tip simplă ori compusă.
Forma generală a unei instrucțiuni while este:
while (expresie)
instrucțiune;

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.

Exemplu instrucțiune while


În cadrul acestui exemplu se va calcula factorialul numărului n.
f=1; i=1;
while (i<=n)
{
f*=i;
i++;
}

3.3.4. Instrucțiunea do-while (structură repetitivă condiționată posterior)


Spre deosebire de instrucțiunea while, do-while execută instrucțiunea specificată cel puțin
o dată, chiar dacă expresia are de la început valoarea 0, deoarece evaluarea acesteia se face după
executarea instrucțiunii. Aceasta este singura diferență dintre cele două instrucțiuni.

6
PC 1 – CURSUL 3 SILVESTRU CĂTĂLIN

Forma generală a unei instrucțiuni do-while este:


do
instrucțiune
while (expresie);

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.

Exemplu instrucțiune do-while


Determinarea numărului de cifre al unui număr natural n:
nr=0;
do
{
n/=10;
nr++;
}
while (n);

Î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ă!

3.3.5. Instrucțiunea for (structură repetitivă cu numărător)


Această instrucțiune poate fi văzută ca o instrucțiune de tip while, ce prezintă mai multe
expresii de luat în calcul (precum: inițializare, continuare și reinițializare). Față de instrucțiunea while,
instrucțiunea for este un instrument mai puternic și flexibil ce permite exprimarea mai concisă a
prelucrărilor de natură repetitivă.

7
PC 1 – CURSUL 3 SILVESTRU CĂTĂLIN

Forma generală a unei instrucțiuni for este:


for (expresie_inițializare; expresie_continuare; expresie_reinițializare)
instrucțiune;

Ca efect, sunt urmați trei pași:


1. evaluarea expresiei de inițializare;
2. evaluarea expresiei de continuare;
3. dacă valoarea expresiei de continuare este nulă, se iese din instrucțiunea for. În caz
contrar:
- se execută instrucțiunea;
- se evaluează expresia de reinițializare;
- se revine la punctul 2.
Același lucru îl putem scrie și cu ajutorul unei instrucțiuni de tip while:
expresie_inițializare;
while (expresie_continuare)
{
instrucțiune;
expresie_reinițializare;
}

Utilizarea instrucțiunii continue; în instrucțiunea for are ca efect abandonarea iterației


curente și trecerea la evaluarea expresiei de reinițializare..

Exemplu instrucțiune for


Revenim la exemplul prezentat în cadrul instrucțiunii while – calculul factorialului unui număr
n; de această dată însă scris cu ajutorul instrucțiunii for:
for (f=i=1; i<=n; i++)
f*=i;
ori o variantă mult mai restrânsă

8
PC 1 – CURSUL 3 SILVESTRU CĂTĂLIN

for (f=i=1; i<=n; f*=i++)

Î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

2.1. Tipuri de date

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:

- baza 2 (decimal – notație ce utilizează doar cifrele 0 și 1);


- baza 8 (octal – notație ce utilizează doar primele 8 cifre și este precedată de un 0
nesemnificativ);
- baza 10 (zecimal - notația uzuală cu 10 cifre);
- baza 16 (hexazecimal – notație ce utilizează primele 10 cifre plus încă 6 caractere în
ordine: A, B, C, D, E, F, iar constanta are prefixul 0x sau 0X).
Pentru a putea realiza transpunerea unei constante dintr-o bază în alta, este necesară
cunoașterea regulii de reprezentare a numerelor în baza b, unde b reprezintă baza sistemului de
numerație (b∈ℕ, b>1).

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 … 𝑥1 𝑥0 = 𝑦1 𝑦2 … 𝑦𝑝(𝑏) = 𝑥𝑛 ∙ 𝑏 𝑛 + 𝑥𝑛−1 ∙ 𝑏 𝑛−1 + ⋯ + 𝑥1 ∙ 𝑏 + 𝑥0 + 𝑦1 ∙ 𝑏 −1 + ⋯ + 𝑦𝑝 ∙ 𝑏 −𝑝

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.

Conversia numerelor din baza 10 în baza b

Regula de conversie a numerelor naturale:

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).

Regula de conversie a numerelor subunitare:

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).

Regula de conversie a numerelor reale:

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

Conversia numerelor din baza b în baza 10

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:

2BD(16)=2∙162 + B∙161 + D∙160=2∙162 + 11∙16 + 13∙1=701(10);

10110,011(2)=1∙24 + 0∙23 + 1∙22 + 1∙21 + 0∙20 + 0∙2-1 + 1∙2-2 + 1∙2-3=22,125

Conversia numerelor din baza b în baza b`

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`.

Tipul de date int


Datele de tip int reprezintă mulțimea numerelor întregi, cuprinse în intervalul [-32768,
32767], reprezentate în memorie pe 2 octeți, în cod complementar.

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:

 mai întâi, se reprezintă în binar valoarea sa absolută (247): 1111 0111;


 calculăm apoi complementul față de 28 prin scădere binară, 8 poziții fiind suficiente pentru
a reprezenta numărul -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:

Tabelul 2.1. Datele de tip int

Tip Interval de valori Reprezentare


int [-32.768, 32.767] 2 octeți, cu semn
unsigned [0, 65.535] 2 octeți, fără semn
long [-2.147.483.648, 2.147.483.647] 4 octeți, cu semn
unsigned long [0, 4.294.967.295] 4 octeți, fără semn

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.

Tabelul 2.2. Datele de tip char

Tip Interval de valori Reprezentare


char [-128, 127] 1 octet, cu semn
unsigned char [0, 255] 1 octet, fără semn

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’, ‘*’, ‘ ‘, ‘$’).

Caracterele negrafice se pot specifica încadrând între apostrofuri o secvență de „evitare”


(escape). Această secvență se formează cu ajutorul caracterului backslash (‘\’), urmat de codul
ASCII al caracterului (exprimat în baza octală sau hexazecimală, precedat de x).

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

Tabelul 2.3. Caractere asociate secvenței escape

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

Constantele ce reprezintă un șir de caractere sunt constituite dintr-o succesiune de caractere


încadrată între ghilimele (ex: “sir de caractere”, “Linia 1 \n Linia 2” (în al doilea
exemplu pe ecran se vor afișa cele două șiruri unul sub celălalt datorită secvenței \n)).

Tipul real
Tipul real de date în limbajul C/C++ sunt definite ca float și double, dintre care double
acceptă și modificatorul long.

Tabelul 2.4. Datele de tip real

Tip Interval de valori Reprezentare


float -38 38 38
[3.4∙10 , 3.4∙10 ]∪[-3.4∙10 , -3.4∙10 ] -38
4 octeți, în virgulă mobilă
double -308 308
[1.7∙10 , 1.7∙10 ]∪[-1.7∙10 , -1.7∙10 ]308 -308
8 octeți, în virgulă mobilă
long double [3.4∙10-4932, 1.1∙104932]∪[-3.4∙104932, -1.1∙10-4932] 10 octeți, în virgulă mobilă

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:

1.3e-10 (în format uzual: 0,00000000013) = 1,3 ∙ 10-10;

-3.29E+6 (în format uzual: -3290000) = 3,29 ∙ 106.

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:

tip_variabilă nume_variabilă[=expresie1] [, nume_variabilă2[=expresie2] ...];

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

Exemplu de declarare a variabilelor în C/C++


int a, b=7, c=1+2;

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

Preprocesorul este un program lansat automat în execuție înainte de compilare. Acesta


execută toate directivele preprocesor incluse în program, efectuând substituții de texte.

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.

Pentru a include într-un program un header standard se operează astfel:

#include <nume_fisier_antet.h>

//respectiv

#include “nume_fisier_antet.h”

Vom prezenta 3 cele mai folosite astfel de exemple:

#include <stdio.h> /*include un header al bibliotecii limbajului C cu funcții de


intrare/ieșire*/

#include <iostream.h> /*include un header al bibliotecii limbajului C++ cu funcții de


intrare/ieșire*/

7
PC 1 – CURSUL 2 SILVESTRU CĂTĂLIN

#include <math.h> /*include un header al bibliotecii de funcții matematice*/

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:

#define identificator_constantă valoare

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.

Exemple de utilizare a directivei define:

#define PI 3.14

#define e 2.71

#define NotaMAX 10

Fișierele antet conțin și definiții de constante simbolice:

 î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.

2.3. Utilizarea funcțiilor din bibliotecile standard

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);

Exemple de biblioteci uzual folosite


 math.h – conține funcții matematice complexe precum extragerea radicalului
(sqrt()). Apelarea acestei funții se realizează astfel:
float sqrt(float x);

Se va realiza astfel calcularea radicalului valorii x, transmise ca parametru.

 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):

void textbackground(int culoare);

Spre exemplu, dorim să schimbăm fundalul ecranului în albastru. Este necesar să


apelăm mai întâi funcția textbackground() pentru stabilirea culorii, iar apoi
funcția clrscr() pentru a realiza colorarea ecranului în culoarea de fundal;

textbackground(BLUE);

clrscr();

9
PC 1 – CURSUL 2 SILVESTRU CĂTĂLIN

2.4. Citirea și scrierea în C++

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.

În header-ul iostream.h sunt declarate două „fluxuri” standard:

 de intrare de la tastatură – cin (console input);


 de afișare pe ecran – cout (console output).

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;

De asemenea, este important de reținut faptul că la citirea cu ajutorul operatorului de


extragere, valorile numerice necesar a fi citite de la tastatură se separă prin caractere albe, acestea
fiind ignorate de operatorul de citire. În timpul citirii, se vor introduce datele de la tastatură,
finalizarea introducerii acestora fiind semnalată prin acționarea tastei Enter.

10
PC 1 – CURSUL 2 SILVESTRU CĂTĂLIN

Exemple de citire în C++


#include <iostream.h>

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;

Prin urmare, expresia va fi evaluată și convertită într-o succesiune de caractere ce va apărea


pe ecran. Asemenea operatorului de citire, acesta poate fi utilizat în mod înlănțuit pentru afișarea
simultană a mai multor date:

cout<<expresie_1<<expresie_2<<...<<expresie_n;

Exemplu de afișare în C++


#include <iostream.h>

int a;

int main(){

int b;

cout<<”b=”; cin>>b;

11
PC 1 – CURSUL 2 SILVESTRU CĂTĂLIN

cout<<”Valoarea lui b este ”<<b<<’\n’;

cout<<”Valoarea lui a este “<<a<<endl;

return 0;

În cadrul acestui exemplu, am început desigur prin apelarea bibliotecii iostream.h,


necesară operației de citire/scriere a datelor. Am declarat apoi variabila a înafara funcției main(),
prin urmare a devenind o variabilă globală. În cadrul funcției main() a fost declarată local și
variabila b. Programul a afișat apoi mesajul „b=”, indicând utilizatorului programului faptul că acesta
trebuie să introducă valoarea variabilei b. Valoarea va fi preluată de la tastatură de către program și
reținută în memorie. Programul va trece la pasul următor în care afișează un mesaj specific, urmat de
valoarea variabilei b. Imediat după aceea va muta cursorul pe linia următoare datorită instrucțiunii
’\n’. Pe linia următoare va afișa un alt mesaj dedicat variabilei a, urmat de valoarea acestei variabile
(variabilă ce a fost inițializată cu 0 automat, datorită declarării acesteia la nivel global). Imediat după,
cursorul va fi mutat pe următoarea linie datorită instrucțiunii ’endl’ (instrucțiune asemănătoare cu
’\n’).

2.5. Citirea și scrierea în C

Î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:

scanf(format, adresă_variabilă_1, adresă_variabilă_2, ..., adresă_variabilă_n);

În urma apelării, funcția va parcurge succesiunea de caractere introdusă de la tastatură și


extrage valorile necesar a fi citite conform formatului specificat. Valorile citite sunt memorate în
ordine în variabilele alocate, specificate prin adresa lor în memorie. Adresa unei variabile se obține cu
ajutorul operatorului de referință (’&’), operator unar cu un singur operand de tip variabilă (ex:
&variabilă). De asemenea, funcția scanf() returnează numărul de valori citite corect, iar în cazul
în care apare o eroare în timpul citirii, operația de citire se va întrerupe.

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

Parametrul format este reprezentat de un șir de caractere ce poate conține specificatori de


format, caractere albe și alte caractere. Asemenea citirii în C++, caracterele albe vor fi ignorate,
celelalte caractere fiind obligatoriu prezente în pozițiile corespunzătoare. Specificatorii de format au
următoarea sintaxă:

%[*][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:

Tabelul 2.5. Literele tip din specificatorul de citire

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.

Formatul celor două funcții este:

int getch(void);

int getche(void);

Exemple de citire în C
 Fie următoarele declarații de variabile:
int a; unsigned b; double c;

De la tastatură se introduc caracterele: 123 -2.1 900000. Prin apelarea funcției:

scanf(”%d %lf %u”, &a, &c, &b);

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!)

 Fie următoarele declarații de variabile:


char C1, C2, C3;

De la tastatură se introduc caracterele: pi3. Prin apelarea funcției;

scanf(”%c%c%c”, &C1, &C2, &C3);

variabilei C1 i se va atribui caracterul ’p’, variabilei C2 caracterul ’i’, iar variabilei C3 caracterul ’3’.

 Fie următoarele declarații de variabile:


char C1, C2; unsigned long n;

De la tastatură se introduce: 421 xD. Prin apelarea funcției:

scanf(”%lu%c%c”, &n, &C1, &C2);

variabilei n i se atribuie valoarea 421, variabilei C1 caracterul ’ ’, iar variabilei C2 caracterul ’x’.

Prin apelarea funcției:

scanf(”%lu %c %c”, &n, &C1, &C2);

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

 Să presupunem că de la tastatură se introduce o ecuație matematică de genul: 4=1+2. Prin apelarea


funcției:
scanf(”%d=%d+%d”, &x, &y, &z);

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.)

Prin apelarea funcției:

scanf(”%d=%*d+%d”, &x, &y);

variabila x va avea valoarea 4 iar variabila y valoarea 2. Caracterul 1 a fost citit, însă nu a fost atribuit
nici unei variabile.

 Să presupunem că de la tastatură se introduce data 28-01-1999 (dată în formatul zz-ll-aaaa). Pentru


a citi ziua, luna și anul putem apela funcția:
scanf(”%u-%u-%u”, &zi, &luna, &anul);

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ă:

printf(format, expresie_1, expresie_2, ..., expresie_n);

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;

Precum și la formatul de citire, specificatorul de format de afișare începe cu caracterul ’%’,


urmat obligatoriu de o literă ce indică tipul de date al expresiei căruia se adresează, plus eventual între
aceste 2 alte elemente opționale. Literele tip pot avea următoarele forme:

15
PC 1 – CURSUL 2 SILVESTRU CĂTĂLIN

Tabelul 2.6. Literele tip din specificatorul de afișare

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

Vom da câteva exemple de afișări pe baza următoarelor variabile:

int x=-10, y=01234, z=0xB01A;

float a=123.456, b=-0.07;

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:

 operatorul simplu de atribuire ’=’;


 operatori de atribuire compuși de forma ’operator=’, unde ’operator’ poate fi
oricare dintre caracterele {+, -, *, /, %, <<, >>, &, |, ^}.

Formatul în care se utilizează corect acești operatori este:

variabilă = expresie;

se evaluează expresia, apoi se atribuie variabilei valoarea expresiei.

variabilă operator= expresie;

este echivalent cu

variabilă = variabilă operator expresie;

De asemenea, expresia poate fi la rândul ei o expresie de atribuire, având astfel posibilitatea


de a utiliza înlănțuit operatorii de atribuire:

variabila1 = variabila2 = ... = variabilan = expresie;

Clasa de prioritate a acestor operatori este 15.

Exemple operatori de atribuire


int a=10, b;

long r=101000;

float x, y=7;

Expresie Rezultat Observații


a+=5 15 Valoarea variabilei a este adunată cu 2
x=(a+r)/2 50505 Se atribuie câtul împărțirii întregi a lui a+c la 2 variabilei x
b=a*=3 30 Valoarea variabilei a se înmulțește cu 3, rezultat atribuit apoi variabilei b
a=y/3 2 Variabilei a i se atribuie partea întreagă a valorii de tip float y/2

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:

expresie_1, expresie_1, ..., expresie_n;

Evaluarea se realizează de la stânga la dreapta, valoarea întregii expresii fiind egală cu


valoarea rezultată în expresie_n.

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.

Exemplu de operator virgulă


int a, b, c;

Expresie Rezultat Observație


Variabila a primește valoarea 0, variabila b primește valoarea 2,
a=0, b=a+6, c=b/2; 3
iar variabila c primește valoarea 3

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.

Tabelul 2.7. Operatorii aritmetici

Operator Denumire Tip Clasă


’+’, ’-’ semna algebric unari 2
’*’, ’/’, ’%’ multiplicativi binari 4
’+’, ’-’ aditivi binari 5

19
PC 1 – CURSUL 2 SILVESTRU CĂTĂLIN

Exemple operatori aritmetici


int a=5, b=7;

float r=1.5;

Expresie Rezultat Observații


a/2 2 Câtul împărțirii întregi a lui a la 2
r/2 0.75 Câtul împărțirii reale a lui r la 2
b%2 1 Restul împărțirii întregi a lui b la 2
r%2 Error Operatorul ’%’ se aplică doar operanzilor întregi
(a+b)/2 6 Media aritmetică dintre a și b

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.

Exemple operatori de incrementare/decrementare


int a=0;

unsigned b=4;

float c=3.2;

Expresie Rezultat Observații


c++ 4.2 Se mărește valoarea variabilei c cu 1
--b 3 Se micșorează valoarea variabilei b cu 1
int x=--a; -1 Variabila x primește valoarea -1
int x=a--; 0 Variabila x primește valoarea 0

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

Exemple operatori de egalitate


Expresie Rezultat Observații
5==2+3 1 Operanzii sunt în relația de egalitate
7!=3+4 0 Operanzii nu sunt în relația de inegalitate

Operatori raționali
Aceștia sunt operatori binari ce au ca efect desemnarea relației de ordine în care se găsesc doi
operanzi:

 mai mic – ’<’;


 mai mare – ’>’;
 mai mic sau egal – ’<=’;
 mai mare sau egal – ’>=’.
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
7.

Exemple operatori raționali


Expresie Rezultat Observații
1>3 0 Operanzii nu sunt în relația de "mai mare"
4<=3+5 1 Operanzii sunt în relația de "mai mic sau egal"

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:

expresie_1 ? expresie_2 : expresie_3;

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.

Clasa de prioritate a acestor operatori este 14.

21
PC 1 – CURSUL 2 SILVESTRU CĂTĂLIN

Exemple operatori condiționali


Expresie Rezultat
a>b ? a:b maximul dintre a și b
a>=0 ? a:-a modulul lui a

Operatorul de adresă (referință)


Acest operator unar permite determinarea adresei zonei de memorie în care este stocată o
variabilă (întâlnit mai devreme la funcția de citire):

&variabilă

Operatorul de conversie explicită


Acest operator unar permite conversia forțată a tipului de date al unei expresii la un tip
specificat:

(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ă.

Exemplu de operatori de conversie explicită


int a=5, b=10;

Expresie Rezultat Observații


Media aritmetică a celor 2 variabile ar fi fost de tip întreg (8) în
((float)a+b)/2 7.5
absența conversiei de tip real

Operatorul de determinare a dimensiunii


Acest operator unar determină dimensiunea exprimată în număr de octeți a zonei de memorie,
necesare pentru stocarea expresiei specificate ca operand ori a unui tip de date. Formatul acestui
operator este:

sizeof (expresie);

sizeof (tip);

22
PC 1 – CURSUL 2 SILVESTRU CĂTĂLIN

Exemple de utilizare a operatorilor de determinare a dimensiunii


int a;

Expresie Rezultat Observație


sizeof(a); 2 O variabilă de tip int memorată pe 2 octeți
sizeof(float); 4 O variabilă de tip float se memorează pe 4 octeți

Operatori logici globali


Operatorii logici globali pot fi de 3 tipuri:

Tabelul 2.8. Operatorii logici globali

Operator Denumire Tip Clasă


’!’ not (negație logică) unar 2
’&&’ and (conjuncție logică) binar 12
’||’ or (disjuncție logică) binar 13
După cum se știe, valoarea 0 este asociată valorii logice „fals”, oricare altă valoare având
semnificația de „adevărat”. Astfel, efectul operatorilor logici globali respectă aceeași logică ca în
matematică:

x 0 ≠0 && 0 ≠0 || 0 ≠0
!x 1 0 0 0 0 0 0 1
≠0 0 1 ≠0 1 1

Exemple operatori logici globali


Expresie Rezultat
adevărat (1) pentru x nr. par
!(a%2)
fals (0) pentru x nr. impar
adevărat (1) pentru x∈[a, b]
(x>=a) && (x<=b)
fals (0) pentru x∉[a, b]
adevărat (1) pentru x∉[a, b]
(x<a) || (x>b)
fals (0) pentru x∈[a, b]

Operatori logici pe biți


Aceștia sunt operatori pe biți ce se aplică doar operanzilor de tip întreg și, au ca efect
aplicarea operațiilor logice (negație, disjuncție, conjuncție, disjuncție exclusivă) bit cu bit. Aceștia
sunt:

23
PC 1 – CURSUL 2 SILVESTRU CĂTĂLIN

Tabelul 2.9. Operatorii logici pe biți

Operator Denumire Tip Clasa


’~’ negația pe biți unar 2
’<<’, ’>>’ deplasare la stânga respectiv deplasare la dreapta binar 6
’&’ conjuncție logică pe biți binar 9
’^’ disjuncție exclusivă pe biți binar 10
’|’ disjuncție logică pe biți binar 11

Efectele operatorilor pe biți:

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.

Exemple operatori logici pe biți


Se consideră două variabile întregi declarate în hexazecimal, cu următoarele lor reprezentări
în memorie:

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).

Tabelul claselor de prioritate


Acum că am parcurs toți acești operatori, îi putem grupa într-un tabel ordonat pe clasele de
prioritate ale operatorilor:

Tabelul 2.10. Clasele de prioritate ale operatorilor

Clasa Operatori Asociativitate


2 ’!’ ’~’ ’+’ ’-’ ’++’ ’--’ (tip) ’sizeof’ ’&’ dreapta → stânga
4 ’*’ ’/’ ’%’ stânga → dreapta
5 ’+’ ’-’ stânga → dreapta
6 ’<<’ ’>>’ stânga → dreapta
7 ’<’ ’>’ ’<=’ ’>=’ stânga → dreapta
8 ’==’ ’!=’ stânga → dreapta
9 ’&’ stânga → dreapta
10 ’^’ stânga → dreapta
11 ’|’ stânga → dreapta
12 ’&&’ stânga → dreapta
13 ’||’ stânga → dreapta
14 ’?’ ’:’ dreapta → stânga
’=’ ’*=’ ’/=’ ’%=’ ’+=’ ’-=’ ’&=’ ’^=’ ’|=’
15 dreapta → stânga
’<<=’ ’>>=’
16 ’,’ stânga → dreapta

25
PC 1 – CURSUL 1 SILVESTRU CĂTĂLIN

Cursul 1
BAZELE SISTEMELOR DE CALCUL ȘI A
LIMBAJULUI C/C++

1.1. Noțiuni introductive

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:

 unitatea de control și comandă (UCC) – ce are rolul de recepționare și transmitere de


informație;
 unitatea aritmetică-logică (UAL) – dedicată executării operațiilor aritmetice și logice,
preluând din memorie valorile operanzilor și mai apoi fiind depuse tot în memorie de către
aceasta rezultatele;
 memoria centrală – pentru stocarea datelor și instrucțiunilor;
 dispozitive periferice – ce au rolul de preluare ori transmitere a informațiilor externe,
alcătuind sistemul de intrare/ieșire.

Figura 1.1. Un sistem de calcul conform arhitecturii von Neumann

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:

 sintaxa – reprezintă totalitatea regulilor pe baza cărora se obțin elementele limbajului;


 semantica – definește semnificația construcțiilor sintactic corecte (a elementelor limbajului);
 pragmatica – definește modul de utilizare a elementelor limbajului.
Se poate observa cum aceste trei aspecte se întâlnesc și în cazul unui limbaj natural (de
exemplu: Limba Română); însă spre deosebire de acestea, utilizarea greșită a unei expresii nu se poate
reinterpreta de către interlocutor ca acesta să ne înțeleagă. Comunicarea cu un calculator trebuie să
respecte cu exactitate aceste reguli de comunicare, acesta nefiind capabil de a face presupuneri ori de a
ghici ceea ce dorim să îi transmitem. Calculatorul va oferi ceea ce îi este cerut – în cazul unei astfel de
greșeli...o eroare.

3
PC 1 – CURSUL 1 SILVESTRU CĂTĂLIN

Calculatorul nu „cunoaște” decât modul său de comunicare denumit cod-mașină – limbajul


procesorului pe care îl are în dotare. Programarea într-un astfel de limbaj necesită cunoașterea
procesorului respectiv. Prin urmare, au fost dezvoltate mai întâi limbajele de asamblare, iar mai apoi
limbajele de programare la nivel înalt (ce nu necesită cunoștințe detaliate referitoare la structura
calculatorului, utilizând notații asemănătoare limbajului natural ori al limbajului matematic).
De-a lungul timpului au fost dezvoltate extrem de multe limbaje de programare, doar unele
totuși fiind capabile de a se impune în timp și ca arie de utilizare, precum: FORTRAN, ALGOL,
COBOL, PASCAL, C, C++, Java, C#. Acestea sunt totuși doar câteva dintre reperele istorice ce au stat
la baza dezvoltării limbajelor de programare. Pe zi ce trece apar noi programe cu noi specificații
destinate noilor ramuri ale tehnologiei (inteligența artificială, limbaje grafice, robotică etc.).
Capitolele ce urmează vor avea în prim-plan studiul limbajului C, precizând eventuale câteva
diferențe față de limbajul C++. Cunoașterea limbajului C reprezintă cea mai mare și importantă piesă
în dezvoltare ca programator, fiind un limbaj de bază, găsit frecvent în ofertele pentru locuri de muncă
și având principiile programării structurale și, ulterior, principiile programării orientate pe obiecte.

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:

 0 – 31: Coduri de control;


 32 – 47: Caractere speciale de pe tastatură;
 48 – 57: Cifrele arabe de la 0 la 9;
 58 – 64: Caractere speciale de pe tastatură;
 65 – 90: Literele mari ale alfabetului latin;
 91 – 96: Caractere speciale de pe tastatură;
 97 – 122: Literele mici ale alfabetului latin;
 123 – 127: Caractere speciale de pe tastatură;
 128 – 255: Caractere grafice.

4
PC 1 – CURSUL 1 SILVESTRU CĂTĂLIN

Tabelul 1.1. Tabelul ASCII

Cod Caracter Cod Caracter Cod Caracter Cod Caracter


0 NULL (nul) 32 (spațiu) 64 @ 96 `
1 START OF HEADER (soh) 33 ! 65 A 97 a
2 START OF TEXT (stx) 34 " 66 B 98 b
3 END OF TEXT (etx) 35 # 67 C 99 c
4 END OF TRANSMISSION (eot) 36 $ 68 D 100 d
5 ENQUIRY (enq) 37 % 69 E 101 e
6 ACKNOWLEDGMENT (ack) 38 & 70 F 102 f
7 BELL (bel) 39 ' 71 G 103 g
8 BACKSPACE (bs) 40 ( 72 H 104 h
9 HORIZONTAL TAB (tab) 41 ) 73 I 105 i
10 LINE FEED (lf) 42 * 74 J 106 j
11 VERTICAL TAB (vt) 43 + 75 K 107 k
12 FORM FEED (np) 44 , 76 L 108 l
13 CARRIAGE RETURN (cr) 45 - 77 M 109 m
14 SHIFT OUT (so) 46 . 78 N 110 n
15 SHIFT IN (si) 47 / 79 O 111 o
16 DATA LINK ESCAPE (dle) 48 0 80 P 112 p
17 DEVICE CONTROL 1 (dc1) 49 1 81 Q 113 q
18 DEVICE CONTROL 2 (dc2) 50 2 82 R 114 r
19 DEVICE CONTROL 3 (dc3) 51 3 83 S 115 s
20 DEVICE CONTROL 4 (dc4) 52 4 84 T 116 t
21 NEGATIVE ACK. (nak) 53 5 85 U 117 u
22 SYNCHRONOUS IDLE (syn) 54 6 86 V 118 v
23 END OF TRANS. BLOCK (etb) 55 7 87 W 119 w
24 CANCEL (can) 56 8 88 X 120 x
25 END OF MEDIUM (em) 57 9 89 Y 121 y
26 SUBSTITUTE (eof) 58 : 90 Z 122 z
27 ESCAPE (esc) 59 ; 91 [ 123 {
28 FILE SEPARATOR (fs) 60 < 92 \ 124 |
29 GROUP SEPARATOR (gs) 61 = 93 ] 125 }
30 RECORD SEPARATOR (rs) 62 > 94 ^ 126 ~
31 UNIT SEPARATOR (us) 63 ? 95 _ 127 Del

5
PC 1 – CURSUL 1 SILVESTRU CĂTĂLIN

Tabelul 1.2. Tabelul ASCII extins conform codificării CP437

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 „+”)

Prenume_Nume Ora Minut (conține spațiu)

_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

Tabelul 1.3. Cuvintele rezervate în C/C++

asm _asm __asm auto break case


cdecl _cdecl __cdecl char class const
continue _cdecl __cs default delete do
double _ds __ds else enum _es
__es _export __export extern far _far
__far _fastcall __fastcall float for friend
goto huge _huge __huge if inline
int interrupt _interrupt __interrupt _loadds __loadds
long near _near __near new operator
pascal _pascal __pascal private protected public
register return _saveregs __saveregs _seg __seg
short signed sizeof _ss __ss static
struct switch template this typedef union
unsigned virtual void volatile while

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: */

//linia de text de mai jos este incorectă și nu reprezintă un comentariu

această linie de program nu este un comentariu și va rezulta într-o eroare

1.4. Separatori

În limbajul de programare este necesară uneori separarea unităților sintactice. Trebuie să îi


specificăm sistemului de calcul unde se termină o instrucțiune ori o declarație, să îi delimităm
caracterele ce reprezintă o constantă, ori un șir de caractere. Pentru aceasta ne putem ajuta de separatori
ori delimitatori.

Există două tipuri de 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.

1.5. Structura generală a unui program C/C++

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.

Exemplu de funcție recursivă


Cel mai comun exemplu de aplicație recursivă este cel de numărare, pe care îl vom explica în
rândurile care urmează. Vom lua spre exemplu procedura de numărare de la 0 la 100 a numerelor
naturale.

Valoarea inițială în cazul nostru este 𝑉0 =


0. Etapa următoare presupune adunarea cu o unitate. După care, se va adăuga o altă unitate la valoarea calculată
100.
V1=V0+1;

V2=V1+1;

...

Vi=Vi-1+1;

...

Vn=Vn-1+1.

Distingem astfel următoarele:


- 𝑉0 = 0, reprezintă formula de start;
- 𝑉𝑖 = 𝑉𝑖−1 + 1, reprezintă formula de recurență pentru 𝑖 = ̅̅̅̅̅
1, 𝑛.

10
PC 1 – CURSUL 1 SILVESTRU CĂTĂLIN

Reprezentarea algoritmilor prin scheme logice


Probabil una dintre cele mai ilustrative și ușor de înțeles reprezentări a algoritmilor este cea în
forma de scheme sau grafuri logice. Un graf poate fi orientat sau neordonat și este o pereche de mulțimi
G=(v, e). Mulțimea V este o mulțime nevidă și finită de elemente numite vârfuri. Mulțimea E reprezintă
mulțimea de perechi neordonate ori ordonate de vârfuri din graf. În cazul în care perechile de vârfuri
din mulțimea E sunt ordonate (deci avem de a face cu un graf orientat), acestea vor lua denumire de
arce. În cazul grafurilor neorientate, perechile de vârfuri din mulțimea E sunt neordonate și denumite
muchii.
Schemele logice fac parte din categoria de grafuri orientate, în care arcele au în vârfuri blocuri
logice. Într-un astfel de graf vom întâlni următoarele categorii de blocuri (instrucțiuni):
4. Blocul START, ce reprezintă punctul de început al schemei logice
START
respectiv al execuției unui algoritm. În schemele logice corecte trebuie să
existe un singur bloc de acest gen.

5. Blocul STOP, reprezintă punctul de final al schemei logice respectiv al


STOP execuției unui algoritm. O schemă logică corectă trebuie să conțină
obligatoriu cel puțin un bloc de acest gen.
6. Blocul de CITIRE, indică citirea uneia sau mai multor valori, denumite
Citește uzual date de intrare. În acest bloc sunt descrise variabilele în cadrul cărora
date_i
se vor aloca valorile citite.

7. Blocul de SCRIERE, indică ieșirea unor valori (mai exact a rezultatelor


Scrie
date_e algoritmului), cunoscute și ca date de ieșire.

8. Blocul de ATRIBUIRE, reprezintă un arc în cadrul căruia se prezintă


v = e
evaluarea unei expresii e și atribuirea rezultatului în locația de memorie
v ← e corespunzătoare unui parametru p. Acest bloc poate fi reprezentat în trei
e → v forme în cadrul schemelor logice.

9. Blocul de RAMIFICARE (selecție), caracterizat prin n arce ce au un


punct comun de plecare. Arcele sunt etichetate cu predicate (ex: c1, c2, ...,
cn) și definite astfel încât:
c1 sau c2 sau ... sau cn = 1 (adevărat) și

ci și cj = 0 (fals) pentru oricare i ≠ j; 𝑖, 𝑗 = ̅̅̅̅̅


1, 𝑛.
Doar un arc poate fi satisfăcut (adevărat)!

11
PC 1 – CURSUL 1 SILVESTRU CĂTĂLIN

În concluzie, o schemă logică este un graf orientat în care:


 Există un singur bloc START și cel puțin un bloc STOP;
 Orice vârf este etichetat cu unul dintre blocurile enumerate mai sus;
 Pentru orice arc există cel puțin un drum care începe cu blocul START și se termină cu un
bloc STOP și conține arcul considerat.

Structurile fundamentale care alcătuiesc schemele logice sunt următoarele:


A. Structură secvențială (liniară) – formată din blocuri distincte succesive conectate prin arce;

Figura 1.2. Structura secvențială

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.

Reprezentarea algoritmilor prin pseudocod


O altă metodă de a descrie un algoritm este prin redactarea acestuia într-un limbaj algoritmic
denumit și pseudocod. Această formă nu are la bază un limbaj anume, fiind necesar doar să
îndeplinească condiția de a conține structuri de bază suficiente spre rezolvarea oricărui algoritm.
Pseudocodul este alcătuit dintr-un număr de instrucțiuni cu ajutorul cărora se pot
descrie/dezvolta algoritmi oricât de complecși. Redactarea acestora fiind asemănătoare cu codificarea
algoritmilor într-un limbaj de programare (de pildă C/C++).
Pentru descrierea algoritmilor sub formă de pseudocod este necesară utilizarea a două categorii
de instrucțiuni:

 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

Descrierea unui algoritm în pseudocod trebuie să fie în corespondență cu configurațiile


desfășurate într-o schemă logică structurată și vice-versa, fiind astfel compatibile cu programarea
structurată. Structura formală a unui algoritm reprezentat prin pseudocod este următoarea:
/comentariu/

declarații de date;

instrucțiune 1;

instrucțiune 2;
...

Instrucțiune n;
stop;

end;

Ilustrarea comparativă a structurilor fundamentale


În figurile următoare sunt comparate structurile de tip scheme logice structurate cu cele de tip
pseudocod.
a) Structura secvențială (liniară);

Figura 1.5. structura secvențială – S.L.S. vs. pseudocod

b) Structuri decizionale (alternative);

Figura 1.6. structura IF-THEN-ELSE – S.L.S. vs. pseudocod

15
PC 1 – CURSUL 1 SILVESTRU CĂTĂLIN

Figura 1.7. structura IF-THEN – S.L.S. vs. pseudocod

Figura 1.8. structura CASE-OF – S.L.S. vs. pseudocod

c) Structuri repetitive.

Figura 1.9. structura WHILE-DO – S.L.S. vs. pseudocod

16
PC 1 – CURSUL 1 SILVESTRU CĂTĂLIN

Figura 1.10. structura DO-UNTIL – S.L.S. vs. pseudocod

Figura 1.11. structura DO-FOR – S.L.S. vs. pseudocod

Reprezentarea algoritmilor sub forma unui program C/C++


Un algoritm scris în una dintre cele 2 forme prezentate anterior se poate transpune cu ușurință
într-un limbaj de programare C/C++. Un program C/C++ este constituit dintr-o succesiune de module
numite funcții. Un program de rezolvare a unei probleme trebuie să conțină obligatoriu funcția
principală, denumită main(). Această funcție specială trebuie inclusă obligatoriu o singură dată în orice
program C/C++, această funcție fiind punctul de START al oricărui program. Pe lângă funcția main()
pot fi apelate (incluse) alte funcții create personal ori deja existente.
Cel mai simplu program în C/C++ poate fi descris astfel:
void main(){

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

S-ar putea să vă placă și