Sunteți pe pagina 1din 72

Proiectul privind Învăţământul Secundar (ROSE)

SCHEMA DE GRANTURI PENTRU UNIVERSITĂŢI – NECOMPETITIVE


Beneficiar: Universitatea „Vasile Alecsandri” din Bacău

Titlul subproiectului:

Matematică, Informatică şi Biologie - educaţie şi profesii pentru viitor.


Rămâi Integrat şi Total Motivat (Mate-Info-Bio-RITM)

Acord de grant nr. 191/SGU/NC/II din 12.09.2019

Material didactic pentru anul I, programul de studii INFORMATICĂ

pentru disciplina

FUNDAMENTELE PROGRAMĂRII
Lect. univ. dr. Iulian FURDU

2019
Câteva cuvinte despre disciplina

FUNDAMENTELE PROGRAMĂRII

Obiectivul principal al acestui curs este de a forma acele idei de bază și abilități
de care studenții (și nu numai) au nevoie în activitatea de programare, pe orice
platformă, indiferent de limbaj. Altfel spus obiectivul disciplinei în termeni de
competenţe profesionale se referă la programarea în limbaje de nivel înalt, în sens
general, în timp ce competenţele specifice (competenţele asigurate de programul de
studiu) vizează: 1. Descrierea adecvată a paradigmelor de programare şi a
mecanismelor de limbaj specifice, precum şi identificarea diferenţei dintre aspectele
de ordin semantic şi sintactic. 2. Explicarea unor aplicaţii soft existente, pe niveluri de
abstractizare (arhitectură, pachete, clase, metode) utilizând în mod adecvat
cunoştinţele de bază 3. Elaborarea codurilor sursă adecvate şi testarea unitară a unor
componente într-un limbaj de programare cunoscut, pe baza unor specificaţii de
proiectare date. 4. Dezvoltarea de unităţi de program şi elaborarea documentaţiilor
aferente.

Cum la Fundamentele programării sunt discutate bazele a ceea ce înseamnă


transformarea unei probleme în algoritm și apoi în cod executabil se poate afirma că
această disciplină are cel puțin legătură cu toate celelalte discipline ce implică
programarea care îi succed: programare orientată obiect, metode de programare,
tehnici avansate de programare, calcul paralel și distribuit, programarea aplicațiilor
mobile etc. Prezentul material ar trebui parcurs secvențial pentru o mai bună
întelegere, resursele indicate fiind în ordinea apariției, materialul având rolul unui
auxiliar pentru partea practică: conține un breviar teoretic aferent fiecărei teme, cu
eventuale resurse adiționale, dublat de un set de aplicații rezolvate. Limbajul folosit va
fi C, excepție intrucțiunile de citire/afișare (cin, cout) ce vor fi preluate din C++.

2
CUPRINS

I. Algoritmi 5

II. Elemente introductive de limbaj C 11

III. Date şi modalităţi de reprezentare a datelor 14

IV. Elementele programării structurate 20

V. Tipuri structurate.Tablouri 30

VI. Structuri 33

VII. Pointeri și referințe 34

VIII. Subprograme 38

IX. Recursivitate 42

X. Șiruri de caractere 46

XI. Fișiere 53

Exemple de subiecte de examen 71

Bibliografie 72

3
4
I. Algoritmi

Noţiunea de algoritm. Caracteristici.


Algoritmul este o noţiune primară, abstractă, neavând o definiţie matematică
riguroasă. Una din formulările acceptate defineşte algoritmul ca o succesiune logică
şi finită de paşi, care, pornind de la o mulţime de valori ca date de intrare produce o
anumită mulţime de valori ca date de ieşire în scopul rezolvării unei clase de
probleme.

Un algoritm poate fi privit ca un instrument de rezolvare a problemelor de calcul


bine definite. Astfel, enunţul problemei specifică relaţia dorită intrare/ieşire, iar
algoritmul descrie o anumită procedură de calcul pentru a se ajunge la această
legătură intrare/ieşire.

Din acest punct de vedere este evident că pentru rezolvarea unei probleme pot
exista mai mulţi algoritmi.

Paşi/Instrucţiuni
Date de Date de
intrare ieşire

Fig.1. Componentele unui algoritm

Distingem trei mari componente ale unui algoritm (fig. 1.):

- date de intrare
- paşii algoritmului sau instrucţiunile
- date de ieşire.
De asemeni, distingem trei tipuri de paşi folosiţi în descrierea unui algoritm (vezi
structuri).

- paşi succesivi sau liniari:


- paşi de decizie
- paşi care determină repetiţia

Problema de care se lovesc în general cei care încearcă să elaboreze un algoritm


este de a identifica în mod corect datele problemei, respectiv cerinţele acesteia şi de
a găsi un model matematic de rezolvare.

Etapele în elaborarea unui algoritm reflectă etapele pe care le întâlnim de altfel în


rezolvarea oricărei probleme:

a.) Înţelegerea enunţului problemei


b.) Delimitarea datelor de intrare (ipoteza) şi a celor de ieşire (concluzia).
c.) Construcţia paşilor algoritmului pornind de la datele de intrare.
d.) Transcrierea algoritmului într-o formă de exprimare.
e.) Testarea corectitudinii algoritmului.

5
Caracteristicile algoritmilor

Sensul unui algoritm este undeva similar cu cel de reţetă, proces, metodă,
tehnică, procedură sau rutină cu deosebirea că el reprezintă un set finit de reguli care
indică o secvenţă de operaţii în vederea rezolvării unei probleme şi are următoarele
caracteristici importante: finitudinea, generalitatea, corectitudinea, unicitatea (v.
resursa [1]).
Descrierea algoritmilor în cadrul programării procedurale se poate realiza prin
diverse modalităţi:
a) schemele logice presupun reprezentarea în mod grafic a paşilor algoritmului,
prin figuri geometrice care să permită o viziune clară, de ansamblu a algoritmului.
Acest mod de descriere este eficient pentru programe nu foarte mari, întrucât o astfel
de schemă devine dificil de citit pe măsură ce numărul de paşi creşte.
b) pseudocod este un limbaj apropiat de limbajul natural, descris de regulă în
limba cea mai apropiată utilizatorului. Descrierea în pseudocod permite o retranscriere
uşoară în diverse limbaje de programare, o exprimare independentă de particularităţile
de limbaj de programare. Pseudocodul este cel mai frecvent utilizat în descrierile
generale ale metodelor şi algoritmilor clasici.
c) limbajele de programare descriu algoritmii folosind un limbaj specific, artificial,
cu o sintaxă şi semantică bine definite
Mai multe: v.resursa [2]. Tot aici găsiți un intro bun pentru mediul de programare
CodeBlocks, folosit la laborator (alternative free: Visual Studio varianta academic,
DevCpp etc.). Urmând instrucțiunile de la resursa [2], creați și compilați programul
Hello world.

Compilator. Interpretor.

Un interpretor este un program translator, care interpreteaza individual comenzile si


corespunzator cu configuratia calculatorului. Translatarea are loc de fiecare dată când
un program este rulat. Interpretorul lucrează repetând următoarele trei operaţii la
întâlnirea fiecarei linii din program: citeşte linia din codul sursă; analizează, verifică şi
codifică binar linia; execută instrucţiunea asociată cu linia interpretată. În acest caz,
limbajul se spune că este interpretat (exemple tipice: Basic, LISP, Prolog, TclTk). Este
posibil ca să existe ambele variante pentru un limbaj, acesta putând fi interpretat şi
compilat (Java). Interpretoarele implementează direct comenzile unui limbaj de nivel
înalt într-un calculator, prin interpretarea a ceea ce cere comanda. Dacă este o buclă,
va fi interpretată de fiecare dată şi calculatorul va da instrucţiuni echivalente. Această
modalitate de lucru poate fi uneori ineficientă şi consumatoare de timp.
Un compilator este un program translator care converteşte programul într-un limbaj
maşină echivalent. Odată tradus, programul poate rula de oricâte ori se doreşte. În
acest caz, se spune că limbajul este compilat (exemple C++, Java, Pascal).
Compilatoarele produc cod maşină echivalent care poate fi optimizat să ruleze eficient.
Majoritatea mediilor de programare au, pe lângă editor de text şi facilităţi de testare şi
„debugging” (depanare), limbajele compilate fiind folosite mai des pentru dezvoltarea
de aplicaţii.
Avantajele limbajelor interpretate: disponibilitatea programului sursă pentru modificări,
realizarea rapidă de programe mici şi execuţia lor. Dezavantajele limbajelor
interpretate provin din faptul că nu există programe executabile, interpretorul trebuie
furnizat cu programul sursă dacă se doreşte executat, sunt mai lente ca programele
compilate. Dezvoltarea unui program compilat durează mult mai mult decât a unuia
interpretat.
Sistemele de operare asigura suport prin interpretoare, compilatoare, assemblere,
linkeditoare, editoare. Aceste programe sistem pot accesa rutine folosite pentru
functiile uzuale (de exemplu citirea sau afisarea unor date), care sunt cuprinse în
biblioteci.

6
Fişier sursă

se includ
Preprocesor
fişierele
header etc.
Compilator

alte coduri Cod obiect Comanda


obiect ”Compile”

link-editor (leagă
toate modulele
obiect)
Comanda
”Run”

Fişier
executabil
date intrare date ieşire
Figura 2. Paşii pentru construirea unui program C (schema simplificată)

Din punct de vedere matematic, limbajul de programare este un limbaj formal,


definit de:

• o mulţime finită de simboluri de bază numită set de caractere sau alfabet;

• o mulţime finită de şiruri construite cu simbolurile de bază , numite cuvinte,


care au asociat un sens şi care alcătuiesc vocabularul limbajului; ca orice
limbă, şi un limbaj de programare dispune de cuvinte proprii predefinite
numite cuvinte cheie sau rezervate (exemple: include, main, int, etc.)

• o mulţime finită de reguli de selectare a cuvintelor pentru generarea de


propoziţii corecte în limbajul respectiv, numită gramatică.

Structura generală a unui program în C

Structura generală a programelor C este următoarea :

zona declaraţii globale = antetul programului (obligatoriu)

afişează-returnează main (lista de parametri)

{ secvenţă de instrucţiuni

="corpul" programului
(zona operativă -
obligatorie)

} // end main

zona declarativă, declaraţii subprograme


7
returnează funcţia_1 (lista de parametri)

} // end functia_1 secvenţă de instrucţiuni

returnează funcţia_2 (lista de parametri)

}// end functia_2 secvenţă de instrucţiuni

returnează funcţia_N (lista de parametri)

} secvenţă de instrucţiuni

Notă: într-un program funcţiile 1...N pot fi scrise şi înainte de main sau pot fi
date doar semnăturile lor, urmând ca focusarea pe detaliile de implementare (codurile
subprogramelor) să aibă loc după main.

Zona de declaraţii a programului

Majoritatea programelor includ iniţial apeluri la biblioteci, respectiv încep cu una


sau mai multe instrucţiuni #include. Instrucţiunea #include (v. directive preprocesare și
ce presupun ele) solicită compilatorului să includă conţinutul unui anumit header
(extensia “.h”). Numele fişierului trebuie încadrat de delimitatorii < > sau de ghilimele
dacă se foloseşte un header care nu aparţine distribuţiei curente.

De exemplu, în programul:

#include <stdio.h>

void main(void)

{ printf("Program in C");
}

i se cere compilatorului utilizarea fişierului stdio.h.


8
Zona declarativă mai conţine:

• declaraţia obligatorie a programului principal: void main() sau int main()


;

• definiţii de constante: const numeconst1 val1; (spaţiu între


numeconst şi valoare) numeconst2 val2;…

• definiţii de tipuri de date: typedef numetip1=tip1;

• declaraţii de variabile globale (recunoscute în tot programul) :

<tip1> numevar1 (sau listă variabile, unde listă variabile reprezintă o mulţime
de variabile de acelaşi tip, separate prin virgulă);

<tip2> numevar2;

• declaraţii de subprograme: proceduri (nu întorc rezultate) şi funcţii


(întorc rezultate).

a) Corpul programului sau zona operativă (secţiunea de program)

Obs. Algoritmul unui program este alcătuit din minim o etapă. În cadrul fiecărei
etape se realizează minim o singură acţiune. Fiecare acţiune (comandă) se realizează
printr-o linie program. Totalitatea acţiunilor unui program, respectiv totalitatea liniilor
program, alcătuiesc corpul programului.

Două sau mai multe linii program poartă numele de secvenţă.

- limbajul C/C++ face distincţie între literele mari şi cele mici


(case sensitive) deci sintaxa trebuie urmărită cu stricteţe.

- pe o linie de ecran se pot scrie una sau mai multe linii


program (instrucţiuni) separate prin caracterul punct şi
virgulă.

Un prim exemplu: transformarea problemei rezolvării ecuației de gradul I din


matematică în informatică, pentru ca apoi să fie trecută în cele 3 forme: schemă logică,
limbaj pseudocod, limbaj de programare.

Matematică: să se rezolve ecuația de gradul I, în forma canonică ax+b=0, a,b ε


R, a≠0.
Separăm termenii:
ax=-b, de unde:
x= -b/a, soluție unică, 0 dacă b=0.
Dacă renunțăm la restricția a≠0 distingem următoarele cazuri:
1. dacă a=0, avem:
1.1. dacă și b=0, avem o infinitate de soluții (0*x+0=0), în caz contrar (b≠0):
1.2. ecuația nu are soluții
2. în caz contrar (a≠0), ecuația are soluție unică în R, x=-b/a.

Informatică: să se rezolve ecuația de gradul I, în forma canonică ax+b=0, a,b ε R.


Traducerea schemei de gândire matematică de mai sus generează:
9
1. schema logică (variantă, vezi resursa [3])

Fig.2. Schema logică, ec. grad I (v.resursa [3])

2. limbaj pseudocod:
1. START
2. CITESTE A,B
3. DACĂ A≠0 ATUNCI
4. CALCULEAZĂ (ATRIBUIE) x=-B/A
5. SCRIE (AFIȘEAZĂ) x
ALTFEL (subînțelegem A=0)
6. DACĂ B=0 ATUNCI
7. SCRIE “EC. NEDETERM sau O INFINITATE DE SOLUTII”
ALTFEL
8. SCRIE “EC. IMPOSIBILA sau EC NU ARE SOLUTII”
9. STOP
Remarcăm lipsa echivalența aproape unu la unu între schema logică si limbajul
pseudocod. Este ALTFEL instrucțiune separată sau face parte din sintaxa DACA (IF)?
Dar ATUNCI?
3. Limbaj de programare (cod C, CodeBlocks, v. resursa [2]) de compilat și rulat
individual:

#include <iostream>

using namespace std;

int main()
{
int a,b;
float x;
cin>>a>>b;
if(a==0)
if(b==0)
cout<<"Ecuatia are o infinitate de solutii.";
else
cout<<"Ecuatia nu are solutii!";
else
{
x=-b/a;
cout<<"Solutia ecuatiei este"<<x;
}
return 0;

10
}

Remarcăm lipsa echivalenței formale unu la unu între limbaj pseudocod și cod C.
Puteți genera cod pentru problema rezolvării ecuației de gradul II?
Soluția:
#include <iostream>
#include<cmath>
using namespace std;
int main()
{ float a,b,c,x,x1,x2,D;
cin>>a>>b>>c;
if(a==0)
if(b==0)
if(c==0)
cout<<"Ecuatia are o infinitate de solutii.";
else
cout<<"Ecuatia nu are solutii.";
else
cout<<"Solutia ecuatiei este:"<<-c/b;
else
{ D=pow(b,2)-4*a*c;
if(D<0)
cout<<"Ecuatia nu are solutii reale.";
else if(D==0)
{ cout<<"Ecuatia are solutii egale: ";
cout<<"x1=x2="<<-b/2*a;
}
else
{ x1=(-b-sqrt(D))/2*a;
x2=(-b+sqrt(D))/2*a;
cout<<"Prima solutie: "<<x1<<endl;
cout<<"A doua solutie: "<<x2<<endl;
}
}
}

II. Elemente introductive de limbaj C

Prezentare generală a limbajului C

În cadrul C se foloseşte setul de caractere ASCII, care asociază fiecărui


caracter un număr întreg din intervalul [0; 127] (la apăsarea unei taste se generează
un cod pe 8 biți, de unde pot exista maxim 28 = 128 - caractere distincte). Caracterul 0
ASCII corespunde caracterului impropriu nul (NULL sau '\0') folosit și pentru a marca
sfârşitul unui string = şir arbitrar de caractere.

Mulţimea caracterelor poate fi impărţită în 3 grupe:

1. Caractere negrafice, care au codurile ASCII corespunzătoare mai mici


decât 32; face excepţie de la această regulă caracterul Delete căruia îi corespunde
127;
2. Spaţiul, care are ca şi corespondent ASCII valoarea 32;
3. Caracterele grafice, care au codurile ASCII mai mari decât 32 (litere mari,
mici, cifre).

11
Literele mari au în ordine codul ASCII de la 65 la 90, literele mici de la 97 la
122, iar cifrele de la 48 la 57.

Obs. Literele (mari, mici) ce fac parte din alfabetul limbii engleze (26 caractere)
au codurile corespondente de mai sus, nu şi diacriticele din limba română. Acestea
aparţin codului ASCII extins ISO 8859-16 ce conţine un total de 256 caractere. Lista
codurilor ASCII se poate găsi la (v.resursa [4])

Specificaţii de limbaj C

Vocabular:

• setul de caractere: a - z , A - Z , 0 - 9;
• semne de punctuaţie: ‘ . ‘ , ‘ , ‘ , ‘ ; ‘ etc.;
• caractere speciale : % , & , $ etc.;
• cuvinte rezervate : while, if, for, else, void, char, int, float, double, unsigned,
include, main, getch etc.
Obs. Există 32 cuvinte rezervate în C. Acestea nu pot fi folosite decât în contextul
pentru care au fost create.

Constante şi expresii

O constantă nu îşi modifică valoarea pe parcursul rulării unui program; pot fi


constante predefinite (INT_MAX, FLOAT_MIN etc) sau constante propriu-zise.
Declararea se face prin “const” înainte sau după specificarea tipului:

float const pi = 3.14;


const float pi = 3.14;
unde “=” semnifică atribuirea.

Obs. Înţelegem prin identificator un nume care defineşte de regulă în mod unic
o entitate a programului: constantă, variabilă, funcţie etc.

Regula de construcţie a identificatorilor valizi. Un identificator conţine doar


litere, cifre şi “_” (underline). Nu poate conţine spaţii şi nu poate începe cu o cifră.

Corect: _alpha, beta_, x1, a_mic. Incorect: o cifra, 1cifra,


o_cifră (conţine diacritice), ocifra-.

Definiţie: O expresie este o construcţie validă în C din punct de vedere sintactic


şi semantic. O expresie poate conţine operatori, operanzi, constante, funcţii şi
paranteze, regulile de evaluare fiind similare celor matematice.

Exemplu: ((a||b)* calcul(pi, 5.2)-3).

Separatori

Definiţie: Un separator separă uzual atomi–lexicali. Cel mai uzual separator


este “spaţiul” iar “ ; “ separă două instrucţiuni.

Alţi separatori: ( ) , [ ] , { } , , (virgulă), “. “ etc.


12
Comentarii: pe o singură linie sunt introduse prin două slash-uri

// <comentariu> în timp ce comentariile pe mai multe linii sunt introduse prin /*


şi */ precum:

/*

<comentariu>

*/“ . Comentariile sunt ignorate de compilator.

Operatori. Operaţii.

Un operator (aritmetic, logic, special etc.) este simbolul sau simbolurile ce descriu
aplicarea unei operaţii, cea corespunzătoare operatorului. Funcţie de context anumiţi
operatori pot avea semnificaţii diferite- respectiv acelaşi simbol poate defini operaţii
diferite. Tabelul cu operatori şi priorităţile lor (v.resursa [5]). Operatorul cel mai uzual
este cel de atribuire.

Precizări.

1. Precedenţa operaţiilor şi particularitatea C care permite ca atribuirea să poată


fi folosită în membrul drept al unei atribuiri face ca pentru: a = 2 - (b = 15); să se
execute: b=15; a= 2-b; De asemeni a=b=c=1 este permisă, evaluarea expresiei
făcându-se în acest caz de la dreapta la stânga. Se atribuie astfel lui c valoarea 1, lui
b valoarea lui c iar în final, lui a valoarea lui b.
2. Se poate folosi forma scurtă (contrasă) a unor operaţii de atribuire:
i-=3 echivalent cu i=i-3

s+=i echivalent cu s=s+i, regula fiind

<expresie1><operator>= <expresie2>

3. Operatorul modulo (%) operează doar asupra întregilor. Împărţirea (/) operează
şi asupra numerelor reale. Rezultatul împărţirii a= 1/2 este 0 chiar dacă „a” a fost
declarat float!. Dacă ambii termeni ai unei împărţiri sunt întregi atunci se face
împărţirea întreagă. Pentru un rezultat corect va trebui ca măcar unul din termeni să
fie scris sub forma unui real, cel mai corect fiind a= 1.0 / 2.0 sau folosirea casting-ului
(vezi conversii de tip).
4. Post şi preincrementare (sau forma prefixată şi postfixată; similar pentru
decrementare)
codul x= i++; este echivalent cu x= i; i= i+1;

iar x= ++i; este echivalent cu i= i+1; x= i; valoarea finală în ambele cazuri


pentru x fiind aceeaşi. Mai concis: în formă prefixată se execută întâi creşterea, apoi
atribuirea în timp ce în forma postfixată se execută întâi atribuirea şi apoi creşterea.
Astfel pentru

y= ((++a)-(b--))*25; avem:
a= a+1;
y= (a – b)* 25;
b= b-1;

13
III. Date şi modalităţi de reprezentare a datelor
Tipuri de date

Un tip de date constă într-o mulţime de valori pentru care există un mod de
reprezentare în memoria calculatorului, cât şi o mulţime de operatori, care pot fi aplicaţi
acestor valori.

Obs. Declararea unui anumit tip de date determină alocarea de către compilator a unei
zone de memorie de lungime specifică tipului de date declarat.

Tipuri de date uzuale: char, int, float, double, void (tipuri fundamentale în C), tipul
tablou, tipul înregistrare, tipul fişier (tipuri structurate).

Clasificarea tipurilor de date în C/C++

Tipuri de bază predefinite (standard) : tipurile întregi (int), tipurile reale (float,
double), tipul caracter (char), tipul logic (bool C++) tipul şir de caractere (string în
C++);

Tipuri structurate: tipul tablou, tipul înregistrare;

Alte tipuri: tipul fişier ( FILE ), tipul adresă, tipul referinţă (pointer) etc.

Tipuri definite de utilizator (cu typedef).

Obs. Tipurile de date pot avea calificatori gen: short, long, etc. Specific tipurilor întregi
sunt calificatorii signed şi unsigned care prefixează tipul. Folosind signed se pot
reprezenta valori pozitive şi negative, în timp de folosind unsigned se pot reprezenta
doar valori pozitive inclusiv zero).

Spaţiu
Denumire Descriere ocupat Domeniu de valori*
(bytes)*
signed: -128 .. 127
char Character 1
unsigned: 0 .. 255

short int signed: -32768 .. 32767


Short Integer 2
(short) unsigned: 0 .. 65535

signed: -2147483648 ..
2147483647
int Integer 4
unsigned: 0 to
4294967295

long int
Long integer 4 signed: -2147483648 ..
(long)
2147483647

14
unsigned: 0 ..
4294967295

Valoare logică: true


bool 1 true sau false
sau false.

Simplă precizie.
float Număr în virgulă 4 +/- 3.4e +/- 38 (~7 digits)
flotantă

Dublă precizie.
+/- 1.7e +/- 308 (~15
double Număr în virgulă 8
digits)
flotantă

long Număr în virgulă +/- 1.7e +/- 308 (~15


8
double flotantă long double digits)

wchar_t Wide character 2 sau 4 1 caracter wide

Observaţii:

1. * Valorile precizate sunt specifice sistemelor cu procesor pe 32 de biţi. Pentru


restul arhitecturilor tipul int are mărimea naturală a sistemului (un "word").
2. Nu toate numerele reale vor fi reprezentate exact, ci cu aproximaţie (cum se
doreşte reprezentarea unui număr infinit de elemente (cuprinse de exemplu într-
un interval), pe un număr finit de “n” biţi, pe care sunt posibile doar 2n
reprezentări distincte.
3. Tipul string nu este un tip fundamental în C în schimb se consideră a fi un tip
fundamental în C++ (de fapt orice string e un şir de caractere gen char text[20]).
4. Secvenţele ESCAPE sunt folosite pentru reprezentarea caracterelor invizibile
sau greu de obţinut. Cele mai uzuale sunt:

Constanta char Denumire Explicaţii

‘ \n ’ LF (line feed) Enter

‘ \t ’ HT (horizontal tabulator) Tab

revenire la capăt de
‘ \r ‘ CR (carriage return)
rând

salt la pagina din


‘ \f ‘ FF
imprimantă

‘ \a ‘ BEL se activează un sunet

‘\‘ single quote afişează un ‘

15
‘\”‘ double quote () afişează un “

‘\?‘ question afişează un ?

Constante

Constantele în C/C++ sunt valori ce nu se modifică în cadrul programului. Există 5


tipuri de constante : întregi, reale, constante caracter, şir de caractere şi constante
simbolice.
Sintaxa: const <tip> < nume > = < valoare > ; sau <tip> const < nume > =< valoare >;
Constantele întregi sunt numere întregi în baza zece sau în baza 16 (ce conţine cifrele
0-9 şi simbolurile A=10, B=11,…F=15). Constantele în baza 16 (hexazecimal) au
sufixul 0x.
Exemple de constante echivalente ca valoare:
60 // zecimal
0074 // octal- baza 8
0x3c // hexazecimal- baza 16
Constantele unsigned pot fi combinte cu anumite sufixe pentru a specifica un tip întreg
specific :
60 // int
60u // unsigned int
60l // long
60ul // unsigned long
60lu // unsigned long
60llu // unsigned long long
Pot fi folosite si majuscule în sufixele de mai sus.

Constantele reale sunt numere reale virgula zecimală fiind înlocuită prin punct. Se pot
defini în două forme :
a) forma fără exponent ce trebuie să conţină minim o cifră atât la partea întreagă
cât şi la cea zecimală 0.0 -12.39 1.0
b) forma cu exponent, când pot lipsi atât punctul cât şi partea zecimală, litera E
(sau D) însemnând "10 la puterea": 34E12 (34E+12 ) sau 34D12 (adică 34*1012), -
45.2E-3 (respectiv –45.2*10 –3 )
 o constantă reală poate fi scrisă în mai multe feluri : 18200 = 18.2E3 = 1.82E4 =
0.182E5 = 1820E1
Funcţie de numărul de octeţi ocupat, constantele reale sunt de două subtipuri :
• float: cuprinse între 1.2 E-38 şi 3.4 E38 ;
• double: cuprinse între 1E-296 şi 3.4E38 ;
Constantele reale pot fi declarate cu sufixul “f”/”F” sau “l”/”L”:
60.328f // float
6.05e-4L // long double

Constante caracter (alfanumerice). O constantă caracter este un caracter ASCII


cuprins între apostrofuri sau ghilimele gen ' a ' sau " a ". Mai multe caractere încadrate
de simbolurile de mai sus formează o constantă şir de caractere .Un şir de
caractere se numeşte string. Stringurile se termină cu un character special ’\0’;

16
Exemple : ' Bit ', " Oberon ". Simbolurile de încadrare nu pot fi alternate pentru
aceeaşi constantă fie ea caracter sau şir de caractere fiind incorect : " program ' sau
invers ' program ". Un şir de caractere nu poate conţine în interior un caracter apostrof
sau ghilimele.
Constante logice.
Există trei constante logice:
bool foo = true;
bool bar = false;
int* p = nullptr; // pointer null
Constante simbolice. O constantă simbolică este desemnată printr-un identificator
ce se poate ataşa unei valori constante de orice tip şi nu poate fi modificat în timpul
execuţiei programului. Constatele de acest tip se numesc şi literali.
Exemple : m=12, max=5 etc.

Există şi constante simbolice predefinite gen INT_MAX = 32767 etc.


-Oricărei valori constante i se poate ataşa o constantă simbolică.
Declararea constantelor se face în secţiunea de declaraţii a programului sub cuvântul
rezervat const
Obs. O constantă se mai poate defini de către utilizator cu #define (exp.: #define
A 1;) ceea ce presupune o înlocuire a textului „A” cu 1 direct în codul sursă fapt ce
creşte rapiditatea execuţiei. În schimb declararea unei variabile drept “constantă” (cu
const) face ca aceasteia să i se aloce memorie, găsirea valorii ei fiind mai lentă; se
face în schimb verificarea tipului, ceea ce creşte robusteţea codului spre deosebire de
situaţia declarării cu #define.
#define linienoua '\n';...
cout<< #linienoua '\n'

Exemplu : const int a = 1; const double PI = 3.14; const int


minim INT_MIN;

-Dacă declarăm mai multe constante cuvântul const se scrie de fiecare dată;
-Se pot declara mai multe constante avănd aceeaşi valoare precum const a,b 3;
-Tipul unei constante (real, caracter, etc.) rezultă din chiar valoarea sa.
Obs. Constantele simbolice (INT_MAX, FLT_MIN etc.) sunt definite conform
standardului C++ în biblioteca <climits.h> însă pot depinde de compilator şi mediu de
programare. Pentru compilatoarele din KDevelop şi Microsoft C++ acestea se află în
limits.h în timp ce pentru distribuţiile Borland C++ Builder sunt în bibilioteca _lim.h. În
DevCpp acestea sunt în limits.h cît şi în values.h.

Variabile

O variabilă, spre deosebire de o constantă, îşi poate modifica valoarea în cadrul unui
program. Ea este caracterizată de:

1. Identificator (nume);
2. Domeniul de vizibilitate (zona de program în care variabila are influenţă/efect )
<engleză: scope >;
OBS. Din acest punct de vedere pot exista într-un program 2 variabile cu acelaşi
identificator ( int a , A = 2 ).
17
3. Valoare;
4. Tipul de date (indică spaţial de memorie alocat/ocupat);
5. Adresă.
O variabilă poate primi drept valoare:

─ o valoare constantă;

Exemplu: A = 2. O altă situaţie în acest caz este declararea cu iniţializare int


A= 2; sau int A(2);

─ valoarea unei constante predefinite sau nu;

Exemplu: #define PI 3.14;


float X = PI;
float X = INT_MAX; //INT_MAX = 2147483647 definit
in //<limits.h>;
─ valoarea întoarsă de o funcţie: m=abs(-11); // abs(x)<- modulul
lui x
─ valoarea unei alte variabile: a = A.
─ valoarea unei expresii (înglobează toate variantele de mai sus):
x = (a+b)&&(!c)||1

Operatori. Expresii.

Limbajul C conţine patru clase de operatori: aritmetici, relaţionali, logici şi de acţiune


la nivel de bit.

Operatorul de atribuire

Ca şi constantele, variabilele pot primi valori la declarare (variabilele gloabale primesc


valori implicite). O variabilă poate primi valori în două moduri: prin atribuire şi prin citire.

Prin atribuire o variabilă primeşte o valoare concretă, bine determinată.

Instrucţiunea de atribuire are sintaxa:

identificator_ de_variabila = expresie sau

nume_funcţie = expresie ;

unde identificator_ de_variabila poate fi o variabilă sau un pointer, nu o funcţie sau o


constantă, iar expresie poate fi o constantă sau o expresie oricât de complexă.

Efect:

1) se evaluează expresia;
2) variabila (funcţia) primeşte valoarea expresiei; tipul expresiei se va converti
înainte de atribuire la tipul variabilei (funcţiei), dacă nu coincide cu acesta. Spre
exemplu, dacă expresia are valoare întreagă şi variabila ce o referă este declarată
reală, se va converti valoarea expresiei în tipul real.
 pentru ca atribuirea să aibă loc trebuie ca tipul variabilei (funcţiei) să fie
compatibil cu valoarea expresiei. Altfel, se semnalează eroarea de sintaxă
"incompatible assignment".

18
 sunt corecte atribuirile de forma x:= x+p, ceea ce s-ar traduce prin : noua valoare
a lui x este egală cu cea veche (anterioară) plus "p".Această nouă valoare este
memorată ca fiind valoarea actuală a lui x, cea veche pierzându-se.
Valoarea atribuită unei variabile poate fi:

a) o valoare constantă, dată direct sau prin intermediul unei constante


simbolice;
b) valoarea altei variabile stabilită anterior;
c) valoarea unei expresii;
d) valoarea returnată de către o funcţie;
e) un set de valori numere întregi într-un interval;

Exemplu comentat:

#include <conio.h>;
#include <math.h>;
const d4; const pi 3.141;
int a,i,j; longint x,y;
char c;
float alfa,aria,s,u ,c1,c2;
bool er;
void main()
{
a=MIN_INT; /* lui a i se atribuie val constantei predefinite
MININTEGER*/
x=3; //x primeşte val constantă 3
c="t";
y=d; //y primeşte val constantei simbolice d=4
x=y; // x primeşte val variabilei y(d) deci x va fi 4
x=x-1; // x va avea val 3
c=4; c2=5;
alfa=pi/6; //var alfa i se atribuie val expresiei pi/6
u=f.Sin(alfa); // var u ia valoarea returnată de fcţ
sin apelată cu parametrul pi/6, fct predefinită sin se afla în
biblioteca math.h
aria=c1*c2*u/2 ; // aria ia val expresiei c1*c2*u/2
er=x>y; // 3>4 fals, err va avea val FALSE
er=c2 IN n; // TRUE întrucât c2 aparţine domeniului lui n
a=c1/c2; // incorect întrucât "a" a fost declarată de tip
integer iar expresia este de tip real:4/5*)

Conversii de tip

Când variabilele din expresie sunt de tipuri diferite, are loc o conversie de tip, după
regula: valoarea expresiei este convertită la valoarea variabilei din membrul stâng. De
exemplu, în cazul secţiunii

int k;
char ch;
float f;
void func(void)
{
ch=k; /* caz 1*/
k=f; /* caz 2*/

19
f=ch; /* caz 3*/
f=k; /* caz 4*/
}
au loc următoarele conversii de tip:

▪ În cazul 1, cei opt biţi din stânga (de ordin mai mare) sunt eliminaţi şi se atribuie lui
ch cei opt biţi de ordin mai mic. Dacă x este cuprins între 0 şi 255, ch şi k vor avea
valori identice.
▪ În cazul 2, k va primi partea întreagă a lui f.
▪ În cazul 3, valoarea conţinută de cei opt biţi ai lui ch este convertită în virgulă
mobilă.
▪ În cazul 4, valoarea întreagă a lui k este convertită în virgulă mobilă.
Când se converteşte un întreg într-un caracter, se pierd primii 8 biţi, iar când se
converteşte un întreg lung într-un întreg, se pierd primii 16 biţi.

Atunci când într-o expresie sunt amestecate constante şi variabile de diferite tipuri, ele
sunt convertite la tipul cel mai mare de către compilator.

Exemplu: int i;char c;float f; double d;


expresie = (c/i)+(f*d)-(f+i)
c ← int
f ← double
i ← float
int+double ← double
double ← float ← double
Conversia forţată se face specificând tipul dorit între paranteze, în faţa variabilei sau
expresiei ce se converteşte (casting).

Pentru operatori, precedenta lor si aplicatii, consultă resursa [6].

IV. Elementele programării structurate


INSTRUCŢIUNI DE CONTROL

Instrucţiunile de control descriu structurile de control şi fluxul circulaţiei datelor.


Reamintim:

Structura secvenţială: datele sunt parcurse în ordinea prezentării, nu există decizii


sau cicluri, acţiunile se succed, fără a exista condiţionări (pentru ciclu/repetiţie vezi mai
jos).

Fie un set de acţiuni: a1,a2…,an. Fiecare acţiune ai, se desfăşoară necondiţionată de


acţiunea anterioară ai-1. Schematic se poate prezenta astfel:

intrare

a1,

a2,
20
ieşire

Exemplu: se citesc trei două variabile întregi x,y; să se calculeze şi să se afişeze media
lor aritmetică
a1: citeşte x;
a2: citeştey;
a3: atribuie lui m:= (x+y)/2;
a4: afişează m;
Exemplu: să se scrie un program care să schimbe între ele două valori întregi;
aux := x ; // se foloseşte var aux ca variabilă de manevră
x := y ;// acest mod de intershimb se mai numeşte metoda
paharelor
y := aux;

Obs. din punct de vedere al programării structurate secvenţa este tot o structură. Mai
multe instrucţiuni scrise una după alta formează o secvenţă. În Pascal secvenţa este
delimitată de BEGIN şi END, în C, C++, Java, Pearl etc. este delimitată de acolade,
care ţin loc de BEGIN şi END. În Oberon, deoarece fiecare structură, cu excepţia
secvenţei are un END final, secvenţele cuprinse în acea structură sunt precis
delimitate neexistănd necesitatea unui BEGIN, acesta fiind suplinit de însuşi numele
structurii.

Structura decizională (alternativă)

Acţiunile se desfăşoară după un criteriu de selecţie C;


Algoritmul trebuie să răspundă la întrebări de tipul:
-pe care din acţiunile ai o aleg în cazul C?
-acţiunea ai trebuie efectuată în orice condiţie?
Structura generală apare astfel (decizie asupra mai multor cazuri) :

în funcţie de
caz
intrare ieşire
cazul 1: a1,

cazul 2: a2,

1. alternativă binară (instrucţiunea IF);

cond
B A

21
Sintaxă: if <conditie> then A

[else] B

Obs. În cazul în care A sau B lipsesc obţinem o structură decizională cu ramură vidă.

Semantica: se evaluează condiţia. Dacă aceasta este adevărată se execută <A>, iar
dacă nu, se execută <B>;

2. alternativă generalizată (corespunde instrucţiunii switch)


Sintaxa: switch(expresie selector)
{case <const1>:<i1>; break;
case <const2>:<i2>; break;
…………………………..
case <constn>:<in>; break;
[default: < B >]
}

selector
secv 1 secv n+1

secv 2 secv n

Semantica: se evaluează expresia şi se compară rezultatul evaluării cu valorile


constante specificate; se execută secvenţa de instrucţiune asociată k, dacă evaluarea
expresiei coincide cu valoarea constantei din cazul k. Default este opţional.

Obs. Pot exista maxim 256 cazuri (case).

Exemplu: int i;
cin>>i ;
swich(i)
{case 1: {cout<<”Introduceti date“ >>; break;}
case 2: {cout<<”Afisare date”; break;}
default: cin<<”Iesire”;
}
Obs. Prezenţa instrucţiunii break la evaluarea unui anumit caz determină părăsirea
switch, pentru a nu se evalua inutil restul cazurilor. Selectorul trebuie să aparţină unui
tip ordinal.

CONCLUZII :
În cazul structurii decizionale, spre deosebire de structura secvenţială, ordinea
fizică (a scrierii) a prelucrărilor nu mai coincide cu parcursul lor logic;
Structurile liniară şi decizională se pot compune atât în secvenţă (una după
cealaltă), numită compunere prin coordonare, cât şi ierarhic, prin subordonare,
structura decizională conţinând structuri liniare pentru descrierea acţiunilor;

22
Structurile decizionale se pot compune între ele prin subordonare, fiecare bloc
interior legându-se în structură printr-o singură intrare îi o singură ieşire;

INSTRUCŢIUNI REPETITIVE

Clasificare:

A. Instrucţiuni repetitive cu număr necunoscut de paşi:

1. Repetiţia cu test iniţial (while);

2. Repetiţia cu test final (do…while).

B. Instruncţiuni repetitive cu număr cunoscut de paşi:

1. Structura repetitivă cu contor (for):

a. cu contor în creştere ;

b. cu contor în descreştere.

A1. Repetiţia cu test iniţial

Condiţia de
repetiţie

Secvenţa
de
acţiuni

Intrucţiune: while…do

Sintaxa: while(<cond>)
[do]
set instruct <A>
Semantica: se evaluează expresia <cond>: dacă valoarea obţinută în urma evaluării
expresiei este nenulă se execută setul de instrucţiuni A şi se revine la pasul 1, în caz
contrar se trece la instrucţiunea imediat următoare.

Exemplu: Afişarea unui şir de numere naturale de la 1 la n (dat).

Presupunem că în i se depun valorile 1,2,….,n . Iniţial i= 1.

int i=1;
int n=10;
while(i<=n)

23
{cout<<i<<” “ ;
i++;
}
Obs. Setul de instrucţiuni A trebuie să conţină minim o instrucţiune care să afecteze
valoarea testului astfel încât acesta să devină la un moment dat fals (fapt ce determină
părăsirea buclei, altfel are loc o ciclare infinită şi sistemul se blochează).

Exemplu comentat (în Oberon, tocmai ca sa vedeți că nu nu contează sintaxa): Suma


numerelor naturale pare mai mici sau egale cu n, dat.
S=2+4+6+…
pentru n=10 , S=2+4+6+8+10 ; n=7, S=2+4+6.
Calculăm suma într-un ciclu WHILE folosind o variabilă " i ", ale cărei valori vor fi pe
rând termenii sumei 2, 4, 6, …Iniţial i:=2 şi S:=0 ;
Atât timp cât i<=n :
- adaugă la suma S a numerelor pare, un nou termen, şi anume valoarea curentă a lui
" i " : S:=S+i (noul S este vechiul S plus i);
- variabila i (iniţial 2) ia următoarea valoare pară : i:=i+2 ;
 Pentru ca "vechiul S" să existe la execuţia primului pas al ciclului, variabila S, în
care se depune suma, trebuie iniţializată cu 0 ca să nu influenţeze rezultatul adunării
de la primul pas (S=S+i=0+2=2) ;

MODULE sumawhile ; Presupunem că s-a citit n=7:


IMPORT d :=Display ; S:=0; i:=2;
PROCEDURE ProgMain* ; Pas 1: i<=n ? 1<=7 da
VAR s, n ,i :INTEGER; S:=S+i=0+2=2, S=2;i:=i+2=2+2=4,
i=4;
err :CHAR; Pas 2: i<=n ? 4<=7 da
BEGIN S:=S+i=2+4=6, S=6; i:=i+2=4+2=6,
i=6;
Pas 3: i<=n ? 6<=7 da
S:=S+i=6+6=12, S=12; i:=i+2=6+2=8, i=8;
Pas 4: i<=n ? 8<=7 nu => se
iese din ciclu şi se afiseaza ultima valoare a lui S=12

d.WriteStr('n este =');d.ReadInt(n, 3, err);


d.WriteLn();

s := 0 ; i := 2;

WHILE i <= n DO
s :=s+i ;
i :=i+2 ;
END;

d.WriteStr(' suma este = ') ;


d.WriteInt(s, 5) ;
REPEAT UNTIL d.KeyPressed() ;
END ProgMain ;
END sumawhile.
Observaţie : se puteau iniţializa S şi i cu '0' efectul fiind acelaşi doar că se execută un
pas în plus ;
 testarea are loc la început, deci corpul unui ciclu while poate să nu se execute
niciodată, dacă iniţial condiţia este falsă

24
A2. Repetiţia cu test final

Secvenţa
de
acţiuni

Condiţia de
ieşire
repetiţie

Exemplu: do
{cout<<i<<” “;
i++;
}
while(i <= n)
Instrucţiunea: do…while

Sintaxă: do
<B>;
while( <cond> )
instructiunea_urm;
Semantică:

─ se execută setul de instrucţiuni B (1);

─ se evaluează expresia <cond>. Dacă e adevarată se revine la (1), în caz contrar


se execută instrucţiunea următoare;

─ se execută B până când <cond> devine falsă (cât timp cond rămâne adevarată).

Obs:

1.Spre deosebire de while, do…while presupune execuţia cel puţin odată a


setului de instrucţiuni B.

2. Similar cu while şi pentru do...while trebuie să existe în setul B minim o


instrucţiune care să afecteze valoarea testului, altfel se ciclează la infinit.

Exemplu: S = 1 + 2 + … + n
int i=1; s=0;
int n=10;
25
while (i <= n)
{
S = S + i; i++; // S+=i++;
cout<<”Suma la pasul ”<<i<<”: ”<<S<<endl;
}

B. Repetiţia cu număr cunoscut de paşi

contor := vi

v < = vf

Secventa
de actiuni

de repetat

v := v + pas

Instrucţiunea FOR

Sintaxă: for(<expr1> ; <expr2> ; <expr3>)


<set instr A>;
instr_urm;

Semantică:

- <expr1> numită şi expresie de iniţializare; se stabileşte valoarea iniţială a


contorului.

- <expr2> se numeşte expresie de test. Dacă valoarea rezultată a evaluării


expr2 este nenulă, se execută set instr A. În caz contrar se execută instrucţiunea
următoare ( instr_urm ).

- <expr3> = expresie de incrementare/decrementare folosită pentru


creşterea/descreşterea contorului.

Obs. Dacă vi<vf atunci rezultă for cu contor în creştere, în caz contrar for cu contor
în descreştere.

Principiul de execuţie:

- se evaluează expr1;

- se evaluează expr2; dacă s-a produs o valoare diferită de 0 se execută A, altfel se


trece la instrucţiunea următoare;

- se evaluează expr3 şi se revine la pasul 2.


26
Exemplu: for(i=1;i<=n;i++)
cout<<i<<” “;
S=0;
for (i=1;i<=n;i++)
S=S+i;
cout<<”Suma primelor”<<n<<” numere naturale”<<S;
- NU se va altera contorul unui ciclu for în corpul acestuia (prin atribuire sau
citire de la tastatură) ;

Un exemplu complet: algoritmul lui Euclid

Pentru a obţine c.m.m.d.c. a două numere a şi b, folosim:

-împărţim a la b ( b≠0);

-dacă restul1 împărţirii este 0 (r1=0) atunci c.m.m.d.c. este b;

-dacă nu, împărţim pe b la restul împărţirii anterioare (r1) şi obţinem r2;

-împărţim r1 la r2 si obţinem r3 etc.;

-ultimul rest nul este c.m.m.d.c. a lui a şi b.

c.m.m.d.c.(a,b)=c.m.m.d.c.(b,a mod b)

#include <iostream>
using namespace std;
int a, b, deimp, imp, cmmdc, rest;
int main()//void pentru medii
do
{
cout<<”Dati a şi b”<<endl;
cout<<”a= “;cin>>a;
cout<<endl;
cout<<”b= “;cin>>b;
cout<<endl;
deimp=a;imp=b;
do
{rest=deimp%imp;
deimp=imp;
imp=rest;
}
while(imp!=0)
cmmdc=deimp;
cout<<”continuati (d/n)”<<endl;
cin>>ok;
while(ok=”n”)
cout<<”c.m.m.d.c.”<<cmmdc;
system(“Pause”);
getchar();
return 0;
}
Determinarea cmmdc prin scăderi repetate:

# include <iostream.h>
27
# include<conio.h>
int main(){
int a,b,u,v,cmmdc,cmmmc;
cout<<”dati a: “; cin>>a;
cout<<endl;
cout<<”dati b: “; cin>>b;
cout<<endl;
if(a*b==0 ) \\ a sau b sunt 0
{if(a + b != 0)
{cout<<”cmmdc este: “<<a+b;
cout<<endl; }
else
cout<<”cmmmc=0“;
}
else {
u=a;
v=b;
while(a*b)
if(u > v)
u=u-v;
else
v=v–u;
cmmdc=u;
cmmmc=(u*v)/cmmdc;
cout <<”cmmdc= “<<cmmdc<<”cmmmc= “<<cmmmc;
}
system(“pause”);
getch();
}
• Dintre toate structurile prezentate trei sunt cosiderate de bază: structura
secvenţială, structura decizională binară şi structura repetitivă condiţionată anterior,
restul structurilor fiind de fapt structuri auxiliare, acceptate pentru larga lor utilizare,
ce pot fi însă reproduse prin intermediul structurilor de bază.

Aplicații:

1. Determinare parametri cilindru (s. secvențială).


#include <iostream>
#include <math.h>
using namespace std;
float h,r,Ab,Al,A,V;
float pi=3.14;
int main()
{
cout<<"raza ";
cin>>r;
cout<<endl<<"inaltimea ";
cin>>h;
Ab=2*pi*r*r;
Al=2*pi*r*h;
A=Ab+Al;
cout<<"Aria totala "<<A<<endl;
V=pi*r*r*h;
cout<<"Volumul "<<V;
return 0;
}

28
2. Calculul unor sume S=1-1/2+1/3-1/4….(for combinat cu if)
1. #include <math.h>
#include <iostream>
#include <math.h>
using namespace std;
int i,n;float p;
int main()
{ cin>>n;
for(i=1;i<=n;i++)
{ if(i%2==0)
p=p-1.0/i;
else
p=p+1.0/i;
}
cout<<p;
return 0;
}

3. Calculul unor funcții acoladă (if-uri imbricate, determinați singuri forma funcției
după codul de mai jos, compuneți un exemplu nou, domeniul funcției interval închis)

#include <iostream>
#include <math.h>
using namespace std;
float x;float e=2.71;
int main()
{
cin>>x;
if(x>0)
cout<<pow(x,2)-sqrt(x);
else
if(x==0)
cout<<-5;
else
cout<<arcsin(x)+pow(e,x);
return 0;
}
4. Calcul sume, produse, aici suma de la 3 la n din x2/ex (s.repetitive, imaginați alte 2
exemple)
#include <iostream>
#include <math.h>
using namespace std;
int i,n;float p;
int main()
{ cin>>n;
for(i=3;i<=n;i++)
p=p+pow(i,2)/(i-2);
cout<<p;
return 0;}

6. Afișare trunghiuri de numere (structiri repetitive imbricate)


for (i=1; i<=n; i++)
for (j=1; j<=i; j++)
cout<<j;
Ce triunghi se afișează? Scrieți codul celui obținut din acesta prin afișarea în ordine
inversă a liniilor
29
Răspuns:
1
1 2

1 2 … n

V. Tipuri structurate. Tablouri

Un tip de date este structurat dacă este format din tipuri de date elementare, omogen
sau nu.
Cel mai uzual tip stucturat de date este cel de tablou.
Alte tipuri structurate:
- matrici, masive;
- înregistrări, uniuni, fişiere etc.
Observaţii:
1. Generalizarea conceptului de vector poartă denumirea de tablou
(unidimensional = vector, bidimensional = matrice, tri/multi-dimensional = masiv)
Sintaxa:
<tip_vector> <nume_vector>[<dimensiune>]
Exemple: int vector[20]; char alfabet_RO[31];
2. Indexarea elementelor unui vector (ca şi a oricărui tablou în general) începe
în C/C++ de la 0.
3. C/C++ nu face “check boundaries” (nu verifică dacă s-a depăşit dimensiunea
vectorului) astfel că într-o declarare int v[2] folosirea valorii v[2] duce la un
comportament nedefinit al programului; acesta poate suferi un “crash” sau poate
returna o valoare necunoscută.

Eroare clasică:
int v[2] = {1, 3};// declarare vector cu initializare
cout << v[2];

Parcurgere:
Presupunem cunoscută dimensiunea n a vectorului.
Exemplu: for(i=0;i<n;i++)
<prelucrare elemente>
Unde <prelucrare elemente> poate fi:
a) citire:
cin>>vector[i];
b) afişare:
cout<<vector[i];
c) mixt a) şi b).
{cout<<”vector[“<<i<<”]=”;
cin>>vector[i];
cout<<endl;
d) alte prelucrări (vezi mai jos)

Prelucrări vectori
-aritmetice: sumă, produs scalar, norma unui vector etc.
-sortări: bubblesort, sortare cu pivotare, quick sort etc.

Metoda bulelor:
int bule=1;
while(bule = = 1)

30
{ bule=0;
for (i=1; i<=n; i++)
if (v[i] > v[i+1])
{aux= v[i]; // interschimb
v[i]= v[i+1];
v[i+1]= aux;
bule=1;
}
}
Metoda selecţiei directe (sortare cu pivotare)
printf("\n\nVectorul initial:\n");
for(i=0;i<n;i++)
printf("%6.2f",x[i]);
for(i=0;i<n-1;i++)
for(j=i+1;j<n;j++)
if(x[i]>x[j])
{ aux=x[i];
x[i]=x[j];
x[j]=aux;
}
-regăsirea unuia sau a mai multor elemente ale tabloului ce satisfac o sumă de
proprietaţi: regăsirea elementului care are o anumită valoare, determinarea minimului,
a maximului, verificarea dacă elementele vectorului determină o progresie, calculul
numărului de apariţii a unor anumite elemente (pare/impare, a celor divizibile printr-un
număr dat, a acelor elemente care sunt palindroame, a celor care conţin un anumit
număr de vocale etc.).
Exemplu: determinarea apariţiei sau nu într-un vector de 8 elemente a unei valori date
a.
int gasit=0, a=5;
for (i=0; i<=7&&!gasit; i++)
if (v[i]== a) {
cout<<”gasit”;
gasit=1; //determina parasirea buclei
}
if (!gasit)
cout<<”valoarea cautata nu se afla in vector”;

Eroare clasică:
int a = 5;
for (i=0; i<=7; i++)
if (v[i]== a)
cout<<”gasit”;
else
cout<<”negasit”;// va afisa pe rand gasit sau negasit, dupa
caz

-interclasarea: presupune obţinerea din 2 sau mai mulţi vectori sortaţi, un al treilea
format din elementele primilor, în aceeaşi ordine
(crescătoare sau descrescătoare).
while ((i<n) && (j<m))
if (a[i]< b[j])
c[k++]=a[i++];
else c[k++]=b[j++];

Tablou bidimensional: tip de dată corespunzător unei matrici:


<tip_matrice> <nume_matrice>[<nr_linii>][<nr_coloane>]

Citire şi afişare matrici:

31
for (i=0; i<n; i++)
for (j=0; j<n; j++)
{cout << “ a[“ << i << “][“ << j << “]“;
cin>>a[i][j];
}
cout<<endl;
Dacă se doreşte iniţializarea elementelor unei matrici la declararea acesteia:

int m[2][3] = {
{1, -51, 23},
{-2, 7, 8} };
Prin această declarare, elementele matricii mat sunt iniţializate astfel:
mat[0][0]=1, mat[0][1]=-51, mat[0][2]=23
mat[1][0]=-2, mat[1][1]=7, mat[1][2]=8

Obs. Aplicaţiile cu matrici sunt similare cu cele de la vectori.

▪ masive;
Declarare: <tip_masiv> nume_masiv[<dim1>][<dim2>]….[<dimn>]

Cateva aplicații vectori, matrici:


#include <iostream>
using namespace std;
void problema1() // gasirea unei valori inr-un vector, generalizare
{
int n,i,val,k;
cout<<"N=";cin>>n;
cout<<"Valoare=";cin>>val;
int v[n-1];
k=0;
for(i=n-1;i>0&&k==0;i++)
{
cout<<"v["<<i<<"]=";cin>>v[i];
if(v[i]==val)
k=1;
}
cout<<i;
}
void problema2() // calcul suma patrate componente
{
int n,i,s;
cout<<"N=";cin>>n;
int v[n-1];
s=0;
for(i=0;i<n;i++)
{
cout<<"v["<<i<<"]=";cin>>v[i];
s=s+v[i]*v[i];
}
cout<<"Suma="<<s;

}
void problema3() // produsul a 2 matrici, varianta, o puteti
imbunatati?
{
int n,i;
cout<<"N=";cin>>n;
int a[n];
for(i=0;i<n;i++)
{
32
cout<<"A["<<i+1<<"]=";cin>>a[i];
}
int m,mx;
cout<<"M=";cin>>m;
int b[m];
for(i=0;i<m;i++)
{
cout<<"B["<<i+1<<"]=";cin>>b[i];
}
float ps=0;
if(m==n) {
for(i=0;i<mx;i++)
ps=ps+a[i]*b[i];
}
else if(n>m)
{
for(i=0;i<m;i++) ps=ps+a[i]*b[i];
for(i=m;i<n;i++) ps=ps+a[i]*a[i];
}
else
{
for(i=0;i<n;i++) ps=ps+a[i]*b[i];
for(i=n;i<m;i++) ps=ps+b[i]*b[i];
}
cout<<ps;
}
void problema4 () // interschimbare valori vector de la margini
catre mijloc v=(1, 0, 5, 4) devine v= (4, 5, 0, 1). Putem citi
vectorul de la final spre inceput?
{
int aux, n,i;
cout<<"N=";cin>>n;
int v[n-1];
for(i=0;i<=n/2;i++)
{ aux=v[i]; v[i]=v[n-i-1];v[n-1-i]=aux;}
int main()
{
problema1();
problema2();
problema3();
problema4();

VI. Structuri
Obs. Spre deosebire de tablouri care conţin elemente de acelaşi tip, structurile/uniunile
pot conţine variabile diferite reunite sub acelaşi nume.
Structurile sau uniunile sunt tipuri structurate de date.
Variabilele ce alcătuiesc structura se numesc membrii structurii sau câmpuri. Pentru
accesul la un câmp a unei structuri se foloseşte operatorul “ . “ .
Declararea unei structuri se încheie cu “;“ deoarece e considerată o singură
instrucţiune.

Sintaxa:
struct<nume_structura>
{ <tip_camp_1><lista_var_1>;
<tip_camp_2><lista_var_2>;
.

33
.
.
<tip_camp_n><lista_var_n>; }
<lista_var_tip_struct>;
Structuri imbricate: în situaţii în care se declară o structură în interiorul unei alte
structuri.
Exemplu:
struct student {
char nume[30], prenume[30];
struct {
char specializare[20];
float note[14];
} Situatie;
}S1,S2;
Obs. În interiorul structurii “student” se află o structura anonimă ce poate fi apelată
prin intermediul variabilei “Situatie“:
cout<<S1.Situatie.specializare;
cout<<S2.Situatie.note[0];

Definiţie. Uniunile sunt un tip de dată structurată prin care toate câmpurile uniunii
partajează la un moment dat acceaşi locaţie (bloc) de memorie. Dacă o uniune conţine
mai multe câmpuri se alocă uniunii memorie egală cu maximul tipului câmpurilor
componente (cât pentru câmpul de tipul cel mai “mare”). Acest fapt permite ca aceeaşi
zonă de memorie să fie descrisă în mai multe moduri cum fiecare câmp al uniunii este
stocat în aceeaşi locaţie de memorie (de aici şi necesitatea alocării pentru o uniune a
maximului de memorie dintre câmpurile componente). Spre deosebire de uniune, unei
structuri i se alocă memorie egală cu suma câmpurilor componente (câmpurile având
fiecare propria locaţie). O uniune declarată în interiorul unei structuri descrie caracterul
“dinamic” al structurii.
Sintaxa:
union <nume_uniune>
Uniunile sunt în general declarate dacă există înregistrări cu structură variabilă. O
uniune poate fi declarată atât în afara unei structuri cât şi în interiorul ei.
Exemplu:
struct persoana {
char nume[20],prenume[20];
union {
int clase;
struct {
char denumire[20];
char oras[20]
} liceu;
struct {
char denumire[20], oras[20]
} facultate;
} studii;
} p;
p.studii.liceu.denumire sau
p.studii.clase sau
p.studii.facultate.oras
În exemplele de mai sus atribuirile nu pot fi folosite două simultan. Cum aceeaşi zonă
de memorie e partajată pentru toate câmpurile a doua alocare o va afecta pe prima.
Obs. În C structurile nu pot conţine funcţii spre deosebire de C++ unde este posibil.

VII. Pointeri și referințe


Numim pointer o variabilă al cărei atribut valoare poate conţine doar o adresă.

34
Sintaxa: < tip >*< identificator_pointer >

Exemple: int *a,*b; //a şi b pointer către întreg


float *pointer_to_float;
int a1; int *p;
p=&a1; // p primeşte ca valoare adresa lui a1
Obs. Afişarea valorii lui p, cât şi a adresei lui a dau evident acelaşi rezultat.

Avantaj: reutilizarea spaţiului de memorie.

Obs. Pointerii au o aritmetică proprie. O variabilă poate ocupa unul sau mai mulţi octeţi,
în funcţie de tipul său. Un pointer va conţine adresa unei variabile, în fond adresa
primului octet ocupat de aceasta. Dacă pentru o variabilă ştim adresa şi numărul de
octeţi ocupaţi, putem face un calcul prin care, de exemplu, să aflăm adresa următoare.
Procedeu dificil întrucât variabilele pot avea tipuri diferite. Aritmetica pointerilor
simplifică acest calcul:

pointer + 1 → înseamnă adresa următoarei variabile de tipul celei conţinute de


pointer;

pointer + 2 → înseamnă adresa următoare de acelaşi tip.

Legătura pointeri–tablouri:

Obs. C/C++ furnizează două metode de acces la elemenetele unui tablou:

a) specificând indicele elementelor;


b) cu ajutorul pointerilor.
Exemplu: char sir[20];*p;

a) sir[3];
b) p=sir;
c) *(p+3); // vezi si operatii cu pointeri
Obs. p=sir ≡ p=&sir[20].

int *x[ 10 ] → vector de 10 pointeri către întreg.

Obs. Pentru a declara pointeri constanţi folosim:

int b=5;

int *const p=&b;

- p este o constantă de tip integer ce conţine adresa variabilei b (conţinutul său,


respectiv conţinutul de la adresa &b, nu mai poate fi modificat).
O referinţă este un nume alternativ al unui obiect. Sunt utilizate în principal în
specificarea argumentelor şi a valorilor returnate de funcţie, cât şi pentru
supraîncărcarea operatorilor (POO).

Notaţie: &x înseamnă referinţă la obiectul x.

Exemplu: int i=1;


int &r=i; //r şi i se referă la aceeaşi entitate
int x=r;
x++; // i are aceeaşi valoare
r++; // i işi schimbă valoarea

35
Obs. Deşi pentru declararea unei referinţe folosim operatorul &, tipul construcţiei în
care este folosit diferă.

Exemplu: int a=5;


int *pi=&a; // pi = adresa lui a
int &r=a; // r = alt nume pentru a
Obs. Odată ce s-a declarat o referinţă către un obiect (gen int &r=a) aceasta nu mai
poate referi un alt obiect fiind eronată o atribuire precum &r=b;

Obs. O variabilă tip referinţă trebuie iniţializată obligatoriu. Similar un pointer constant
int *const p=&b;

Obs. O referinţă poate fi utilizată ca argument pentru o funcţie care poate să modifice
valoarea acestui argument.

Exemplu: void incr(int &x)


{x++;
}
void f ()
{int i=1;
incr(i);// i creste cu 1, noua valoare a lui i este
definitivă
}
Concluzii. Pointeri versus referinţe.

Declaraţiile:

int a;
int *pa=&a;
şi:

int a;
int &ra=a;
atât pa cât şi ra au ca valoare adresa lui a. Atribuirea unei valori pentru a se poate face
fie &pa=3; fie ra=3; /* se Observa ca la folosirea pointerului adresa se
dereferentiaza folosind operatorul & in timp ce la referinta nu se
foloseste nimic */

Totuşi, în timp ce pentru un pointer se poate efectua pa


++ pentru o referinţă nu este permis. Un pointer poate indica o sumă de obiecte diferite
de-a lungul vieţii în timp ce o referinţă referă un singur obiect. Astfel, în exemplul
precedent, o nouă atribuire pentru ra, &ra=b este greşită. Spre deosebire de stivă pe
heap sunt stocate toate variabilele alocate dinamic cu new (C++), malloc and calloc,
menţinându-se totodată o listă a spaţiului liber care este updatată cu fiecare
alocare/dealocare.

int *pb; //*pb=b; // eroare -> pb este deref inainte de a i


se fi alocat memorie sau valoare
pb = (int*)malloc(sizeof (int)); // fara cast la int malloc
intoarce un pointer către void (type- unsafe) spre deosebire de new
(de preferat) care întoarce un pointer de tipul specificat de operand,
vezi mai jos
*pb=b;
cout<<"&*pb= "<<&*pb<<endl; // adresa la care se afla valoarea
indicata de pb
free(pb); // dealocare

36
Memoria alocată în heap poate fi deci reutilizată pe parcursul rulării programului.
Memoria heap nu este controlată direct de program ci independent prin funcţiile
sistemului de operare sau întreruperi. Obiectele sunt plasate în heap dar referinţele
către ele sunt pe stivă. Zona heap conţine valori reziduale de la folosirea ei anterioară.
Este o memorie care poate fi accesată prin dereferenţierea pointerilor şi nu direct ca
în cazul stivei. De aici necesitatea unui control atent al operaţiilor de alocare,
referenţiere şi dealocare în caz contrar în zona heap putând apărea frecevent scurgeri
de memorie (memory leaks). Folosirea lui „new” în locul unei declarări normale nu
este singura metodă de a muta o variabilă în heap- se mai poate face o variabilă locală
statică (domeniul său de vizibilitate rămâne local) sau globală însă oricine are acces
la ea.

int *p_var = new int(4); // alocare dinamica p_var indica spre un


int de valoare 4, obligatoriu dealocare cu delete
cout<<"p_var: "<<p_var<<endl; // ultima adresa tip int (cea
eliberata prin free(pb)) va fi folosita pt noul pointer
cout<<"*p_var: "<<*p_var<<endl; //4
delete p_var;
int *p_array = new int[50]; // vector alocat dinamic
delete[] p_array; //dealocat
p_array = 0; //

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main() {

char text[20];
char *continut;

strcpy(text, "Buna ziua!");

/* allocate memory dynamically */


continut = malloc( 50 * sizeof(char) );

if(continut == NULL ) {
fprintf(stderr, "Error - unable to allocate required
memory\n");
}
else {
strcpy(continut, "Ziua buna se cunoaste de dimineata!");
}

/* suppose you want to store bigger description */


continut = realloc(continut, 100 * sizeof(char) );

if(continut == NULL ) {
fprintf(stderr, "Error - unable to allocate required
memory\n");
}
else {
strcat(continut, "Doar daca nu ai ore de la 8.00!");
}

printf("Text = %s\n", text );


printf("Continut: %s\n", continut);

/* release memory using free() function */

37
free(continut);
}

Se afişează:
Buna ziua!
Ziua buna se cunoaste de dimineată! Doar daca nu ai ore de la
8.00!

Obs:

1. void* malloc(nr); // aloca un vector de nr de bytes, neinitializaţi

2. void *calloc(int nr, int size); // aloca un vector de nr de elemente, fiecare de mărime size

3. void *realloc(void *adresa, int newsize); // realoca memoria extinzand-o pana la newsize

VIII. Subprograme
În general un subprogram este un modul scris independent de programul principal, dar
legat de acesta printr-un proces de transfer şi revenire.

Definiţie. Procesul de transfer al controlului se numeşte apelare subprogram. Modulul


care solicită execuţia subprogramului se numeşte modul apelant, subprogramul
numindu-se modul apelat.

Un subprogram uzual este format din:

- semnătura: nume urmat de o listă de parametri ce poate fi şi vidă;


- definire subprogram, care poate avea o zonă declarativă (variabile locale) şi zonă
executivă.
Sintaxa declarării unui subprogram:

<tip_rezultat><nume_subprog>(<lista_param_formali>)
Exemplu: int maxim(int a,int b)
bool este_in_sir(double sir[], double x )
void aria(float lung, float latime)
Obs. Spre deosebire de Pascal unde există chiar cuvântul rezervat procedure, în
C/C++ nu există proceduri, ci doar funcţii către void (funcţii care nu întorc nici un
rezultat). Spre deosebire de funcţii, procedurile (în sens clasic) nu întorc în modulul
apelant nici o valoare (rezultat).

Obs. Funcţiile pot conţine minim o singură dată o instrucţiune return. La întânirea
acesteia se părăseşte funcţia cedându-se controlul modulului apelant.

Exemplu: subprogram maxim(int x, int y, int &z)

Apel: maxim(a, b, max);

- a şi b sunt transferate în x, respectiv y;


- subprogramul plasează valoarea mai mare în z;
- valoarea din z este transmisă modulului apelant.

38
Obs. În general entităţile (uzual variabile) declarate în interiorul unui subprogram sunt
entităţi locale – pot fi utilizate doar în interiorul subprogramului respectiv. În cazul în
care unele entităţi trebuie folosite în comun de toate modulele (sau doar de o parte)
unui program, acestea se vor numi entităţi globale.

Obs. Utilizarea entităţilor globale este contraindicată.

Modalităţi de transfer a parametrilor

1) Datele sunt copiate efectiv din locaţiile respective, din programul principal, în
celulele de memorie asociate subprogramului, caz în care se spune că
transferul se realizează prin valoare (ineficient dacă dimensiunea datelor
asociate parametrilor sunt mari, întrucât, lucrându-se cu copii ale parametrilor
reali, stiva alocată subprogramului se încarcă).
Obs. La apel, parametrii formali sunt substituiţi cu cei reali, cele două liste trebuie să
coincidă ca tip şi număr de parametri.

2) Se pot transfera doar adresele celulelor de memorie ce conţin datele


respective, fapt ce oferă subprogramului acces direct la date fără a genera o
copie a acestora.
Se spune că transferul se realizează prin adresă sau referinţă. Parametrii
transmişi prin referinţă vor avea în faţa numelui un & (et).

Exemplu: void unu(int a)


{a = a + 2;
cout<<”unu”<<a;
} // int a=5 ;
void doi(int &b)
{b = b + 5;
unu(b); cout<<”doi1”<<b;
unu(b); cout<<”doi2”<<b;
}
void main()
{int c = 10;
doi(c); cout<<”afara”<<c;}
Obs. În cazul în care apelul unu(b)←unu(a), valoarea lui a trebuie precizată înaintea
apelului (vezi cometariul de mai sus). În această situaţie a devine variabilă globală
pentru “doi“ ca funcţie.

Exemplu:

int x, y, z;
void P(int x, int &y)
{z = x + y;
x++;
y = y + x;
cout<< x<<y<<z<<endl;}
void main()
{
x = 3; z = 4; int t = 10;
P(t,x);
cout<<x<<y<<z;}

void p(int x, int &y)


{
x++; y = x;
}
void pa(int &x, int &y)

39
{
x++; y = x;
}
void main()
{
int x = 0, y;
while(x<3)
{
p(x ,y);
x++;
cout<<x<<y<<endl;
}
x=0;
while(x<3)
{
pa(x ,y); x++; cout<<x<<y<<endl;
}
}

Obs. Dacă mediul nu acceptă tipul logic (bool), bool se înlocuieşte cu integer, true←1,
false←0.

Problemă. Să se scrie un program care să conţină procedura citeşte elementele unui


vector, dimensiunea şi numele lui, o procedură care afişează vectorul, o funcţie care
validează dacă un element aparţine unui vector (mulţimii), o procedură mulţime care
transferă vectorul într-o mulţime, cât şi procedurile reuniune, intersecţie, diferenţă, care
să fie apelate într-un program ce transferă vectorul în mulţimi şi operează cu acestea.

#include<stdio.h>
#include<stdlib.h>
void citeste(int x[], int &n, char nume[])
{int i;
printf(“Dati numarul de elemente pentru vectorul %s”, nume);
scanf(“%d”, &n);
for(i=1; i<=n; i++)
{printf(“Dati %s[%d]= “, nume, i);
scanf(“%d”, &(x[i]));
}
}
void afiseaza(int x[], int n, char nume[])
{int i;
printf(“Vectorul este %s: “, nume)
for(i=1; i<=n; i++)
{printf(“%d”, x[i]);
printf(“\n”);
}
}
bool estein(int a ,int x[], int n)
{int i;
for(i=1; i<=n; i++)
if(x[i] == a)
return true;
return false;
}
void multime(int x[], int n, int y[], int &m)
{int i;
m = 0;
for(i=1; i<=n; i++)
if(!estein(x[i], y, m)
{m++;
y[m] = x[i];
40
}
}
void reuniune(int x1[], int n1, int x[], int n2, int r[], int &nr)
{int i;
for(i=1; i<=n1; i++)
r[i] = x1[i];
nr = n1;
for(i=1; i<=n2; i++)
if(!estein(x2[i], x1, n1)
{nr++;
r[nr] = x2[i];
}
}
void intersectie(int x1[], intn1, int x2[], int n2, int s[], int
&ns)
{int i; ns = 0;
for(i=1; i<=n1; i++)
if(estein(x1[i], x2, n2)
{ns++;
s[ns] = x1[i]; }}
int main()
{int a[20], b[20], am[20], bm[20], reun[40], inters[20],
difer[20];
int na, nb, nam, nbm, nr, ns, nd;
citeste(a , na, “a“);
afiseaza(a, na, “a”);
citeste(b, nb, “b“);
afiseaza(b, nb, “b“);
multime(a, na, am, nam);
afiseaza(am ,nam, “Multime a“);
multime(b ,nb ,bm ,nbm);
afiseaza(bm ,nbm, “Multime b“);
reuniune(am ,nam ,bm , nbm, reun, nr);
afiseaza(reun , nr, “Reuniunea: “);
intersectie(am, nam, bm, nbm, inters, ns);
afiseaza(inters, ns, “Intersectia: “);
diferenta(am, nam, bm, nbm, difer, nd);
afiseaza(difer, nd, “Diferenta: “);
………………
}
Obs. Transferul prin adresă se poate face şi prin intermediul pointerilor.

Exemplu:

void cuPointer(int x, int *y)


{x++;
*y = x;}
int main()
{int x, y;
x = 1;
while(x<=5)
cuPointer(x, &y);
}

Obs. Pointeri la structuri: când structurile sunt mai mari, este mai eficient să se
transmită un pointer către acea structură, iar în interiorul funcţiei să se folosească
accesul prin pointer. Când un pointer al unei structuri e transmis unei funcţii, în stivă
se reţine doar adresa structurii. Acest lucru garantează apeluri de funcţii rapide,
funcţiile putând totodată să modifice conţinutul structurii folosite la apelare.

Exemplu:
41
struct timp
{int ora;
int minute;
int secunde;
}*t;
- apel câmp structură prin pointer

if(t→minute == 60)
{t→minute = 0;
t→ora++;
}
- funcţie simulare potrivire oră la ceas

void potrivire(timp *t) {


if(t→minute == 60)
{t→minute = 0;
t→ora++;
}
if(t→ora == 24)
t→ora = 0;
}
Pointeri către o funcţie

Deşi funcţia nu este o variabilă, ea are localizare în memorie; aceasta poate fi atribuită
unui pointer. Altfel spus, adresa unei funcţii este punct de intrare în funcţie, deci se
poate folosi un pointer către o funcţie pentru a o apela.

Obs. Adresa unei funcţii se obţine specificând numele funcţiei fără paranteze sau
argumente.

Exemplu:

float mult(float x, float y)


{return x*y;}
float add(float x, float y)
{return x+y;}
float scad(float x, float y)
{return x-y;}
float operatie(float(*f)( float , float ), float x, float y)
{return (*f)( x, y )*1000;}

void main()
{float a = 2, b = 3, c;
float(*t[3])(float, float);
t[0] = mult;
t[1] = add;
t[2] = scad;
c = (*t[1])(a, b);
c = operatie(scad, a, b);

IX. Recursivitate
Prin funcție recursivă se înțelege o funcție care se autoapelează, în general cu alt
parametru. Condițiile ca o problemă să se poată rezolva recursiv le găsiți, de exemplu
în resursa [7]. Recursivitatea poate fi directă sau indirectă (determinarea valorilor unei
funcții prin apelul alteia).

42
Obs. Posibilitatea de a rezolva o problema recursiv este o caracteristică a limbajului
de programare şi nu ţine doar de formularea problemei.

Un subprogram recursiv se comportă ca şi cum am avea definite în acelaşi program


un număr de copii identice ale subprogramului.

Calculul factorialului, n!:

I. Mod iterativ:

unsigned long facti(int n)


{int i, unsigned long f=1;
for(i=1; i<=n; i++)
f = f*i;
return f;}

II. Mod recursiv:


unsigned long factr(int n)
{if(n == 0)
return 1;
else
return n*factr(n-1);}

În urma apelurilor recursive, valorile întoarse se refac în ordinea inversă a apelului.

Exemplu:

#include<iostream>
using namespace std;
void frec(int n)
{if(n == 0)
{cout<<”GATA”<<endl;
return;}
cout<<”#n”<<n<<” “<<$n<<endl;
frec(n-1);
cout<<”*n”<<n<<” “<<$n<<endl;
return;}
void main()
{frec(5);…}
Se afişează : #n 5 0012FE98
#n 4 0031DE45
#n 3 00120AB20
#n 2 0011AB19
#n 1 0011AB15
GATA
*n 1 0011AB15
*n 2 0011AB19
. . . . . . .
*n 5 0012FE98

Restricţii specifice recursivitaţii:

Exemplu: int Peano(int n)

{if(n == 1)
return 1;
else
return (1+Peano(n-1));
}

43
Obs. Reamintim: f(1)=1 (funcţia identitate), f(n)=f(n-1)+1.

a) Dacă se renunţă la if (n = = 1) return 1; se obţine: “stack overflow error


“, deci toate valorile admise la apelul unei funcţii recursive trebuie să conducă la
condiţia de oprire.
b) Dacă în loc de if (n == 1) return 1; avem
if (n = = 5)
return 5;
funcţia va rula corect pentru apeluri cu n≥5; în caz contrar nu se va opri.

c) Dacă apelăm funcţia cu o valoare foarte mare putem obţine eroare generată
de depăşirea spaţiului de memorie.

Exemplu:
Peano(214748364) //OK
Peano(412400012) //eroare

Exemple de programe rezolvate recursiv:

1) Aranjamente ( (R)=recursiv, (I)=iterativ,)


R)
int aranjamente(int n, int k)
{
if(k == 0)return 1;
else

return n*aranjamente(n-1, k-1);}


I)
int aranjamente(int n, int k)
{int i; float a = 1;
for(i=n; i>=n-k+1; i--)
a = a*i;
return a;}

2) Combinări
R) int combr(int n, int k)
{if((n == k)||(k == 0))
return 1;
else
return (combr(n-1, k-1) + combr(n-1, k ));}

3) Minimul într-un vector – metoda DIVIDE ET IMPERA


#include<iostream.h>
int n, i, x[20];
int minim(int x[], int inceput, int sfarsit)
{
int m1, m2, mij, m;
if((sfarsit-inceput) == 1)
if (x[inceput]>x[sfarsit])
m = x[sfarsit];
else
m = x[inceput];}
else
{
mij = (inceput + sfarsit)/2;
m1= minim(x,inceput,mij);
m2 = minim(x,mij,sfarsit);
if(m1<m2)
44
m = m1;
else
m = m2;
}
return m;
}

Aplicații:

1. Fibonacci
#include <iostream>

using namespace std;


int fib(int n)
{
if(n==0 or n==1)return 1;
else return fib(n-1)+fib(n-2);
}

int main()
{
int n;
cin>>n;
cout<<fib(n);

return 0;
}

2. Suma elementelor până la n, din 2 în 2.


int suma(int n)
{
if(n==1)return 1;
if(n%2==0) return 0;

else return n+suma(n-2);


}
int main()
{
int n;
cin>>n;
cout<<suma(n);
return 0;
}
3. void oglindit(int n,int &o)// nr oglindit, recursiv
{
if(n!=0)
{
o = o*10 + (n%10);
oglindit(n/10,o);
}
}
int main()
{
int n,o=0;
cin>>n;
oglindit(n,o);
cout<<o;

45
return 0;
}
4. Exemplu recursivitate indirectă
using namespace std;
int an(int n)
{
if(n>0)
{
return bn(n-1);
}
else return 1;
}
int bn(int n)
{
if(n>0)
{
return an(n-1);
}
else return 0;
}
int main()
{
int n,r;
cin>>n;
cout<<"an[1] sau bn[2] ?"<<endl;
cin>>r;
if(r==1) cout<<an(n);
else if(r==2) cout<<bn(n);
return 0;}

Test de autoevaluare cu punctaj. Efectuaţi testul de la


http://www.dponline.ro/articol.php?idarticol=33

O sumă utilă de aplicații rezolvate găsiți la resursa [8].

X. Șiruri de caractere

1. Definirea șirurilor de caractere

Limbajul C nu defineste tipul de data sir, dar exista două posibilități de definire a
șirurilor:

a) ca tablou de caractere;

▪ char sir1[30];
▪ char sir2[15]="exemplul doi";

46
Index 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14

Conținut e x e m p l u l d o i \0 - -

b) ca pointer la caractere;

▪ char *sir3; // sir3 trebuie initializat cu adresa unui sir sau


a unui spatiu alocat pe heap
▪ sir3=sir1; // sir3 ia adresa unui sir static

// sir3=&sir1; sir3=&sir1[0]; sunt echivalente cu instr de


atribuire de mai sus

▪ sir3=(char *)malloc(100);// se aloca dinamic un spatiu pe heap


▪ char *sir4="test";// sir2 este initializat cu adresa sirului
constant

O constanta sir de caractere se reprezinta intre ghilimele.


Ultimul caracter din sir este caracterul nul ('\0'). Ex: "Anul 2001" ocupa 10 octeti de
memorie, ultimul fiind '\0'.

2. Functii de prelucrare a sirurilor

• declarate in stdio.h

1)char * gets(char * s);

citeste caracterele din intrare pina la intalnirea caracterului Enter, care


nu se adauga la sirul s; plaseaza '\0' la sfarsitul lui s; returneaza
adresa primului caracter din sir; daca se tasteaza CTRL/Z returneaza
NULL; codul lui Enter e scos din buffer-ul de intrare

char sir [80]; int n;


printf ("Introduceti un sir: ");
gets (sir);
n = strlen (sir); printf ("Lungimea sirului este: %d
\n", n);

2) int puts(char * s);


tipărește șirul s, trece apoi la rând nou

3) scanf("%s",s);

citeste caracterele din intrare până la întâlnirea primului blanc sau


Enter, care nu se adaugă la șirul s; plasează '\0' la sfârșitul lui s; dacă
se tastează CTRL/Z returnează EOF; codul lui blanc sau
Enter rămân în buffer-ul de intrare

4) printf("%s",s);

tipărește șirul s

• declarate in string.h
47
5) int strcmp(char *s1,char *s2);

returneaza <0, daca s1 < s2

0, daca s1 = s2
>0, daca s1 > s2

6)int strncmp(char *s1,char *s2,int n);

comparare a două șiruri pe lungimea n

7)char* strcpy(char *d,char *s);

copiază șirul sursa s în șirul destinație d; returnează adresa șirului


destinație

8)char* strncpy(char *d,char *s,int n);

copiaza maxim n caractere de la sursa la destinatie; returneaza


adresa sirului destinatie

9)int strlen(char *s);

returneaza lungimea sirului fara a numara caracterul terminator

10)char* strcat(char *d,char *s);

concatenează cele două șiruri și returnează adresa șirului rezultat

11)char* strchr(char s,char c);

returnează poziția primei apariții a caracterului c în șirul s, respectiv


NULL daca c nu e în s

12)char* strstr(char *s,char *ss);

returnează poziția primei apariții a șirului ss în șirul s, respectiv NULL


dacă ss nu e in s.

#include <ctype.h> /* pentru toupper */


n = strlen (sir2);
sir1 = toupper (sir2[n – 1]);

Exemplu: următorul program citeşte de la tastatură un şir de caractere şi afişează


şirul în ordine inversă:

#include <stdio.h>
#include <string.h>
int main (void)
{ char sir [80]; int k, n;
printf ("Introduceti sirul: ");
gets (sir); n = strlen (sir);
for (k = n - 1; k >= 0; k--)
printf ("%c", sir [k]);

48
printf ("\n"); return 0; }

Exemplu: următorul program citeşte un şir de caractere reprezentând un


număr în baza 2 şi afişează valoarea acestuia în baza 10:

#include <stdio.h>
#include <string.h>
int main (void)
{ char sir [80]; int k, n, val, p, cifra, valid;
do
{ do {
printf ("Introduceti sirul: ");
gets (sir); n = strlen (sir); }
while (n = = 0);
valid = 1;
for (k = 0; k < n; k++)
if ((sir [k] != '0') && (sir [k] != '1')) valid = 0;
if (!valid)
printf ("Sirul introdus nu este numar in baza 2 ! \n"); }
while (!valid);
p = 1; /* puterea lui 2 */
val = 0; /* valoarea numarului */
for (k = n - 1; k >= 0; k--)
{ cifra = sir [k] – '0'; val = val + cifra * p; p = p * 2; }
printf ("Numarul %s in baza 10 este: %d \n",sir,val); return 0; }

2. Să se scrie un program care:

• citește cuvintele tastate fiecare pe căte un rând nou, pâna la CTRL/Z (


varianta: pînă la introducerea unui cuvant vid )
• afișează cuvantul cel mai lung
• construieste si afisează cuvântul format din ultima litera din cuvintele
introdusă, transformata in minuscula ( nu vor fi prelucrate cuvintele ce nu se
termina cu o litera )
• afiseaza cuvintele ce incep cu o vocala.

#include <stdio.h>
#include <string.h>
#include <conio.h>
#include <ctype.h>

#define LUNG 81 // lungime max cuvant


#define NR 15 // nr max de cuvinte citite

char tab_cuv[NR][LUNG];
int nr_cuv=0; // numarul cuvintelor introduse

void citire_cuv(void){
printf("*** Se introduc maxim %d cuvinte, terminate cu
CTRL/Z:\n",NR);
while(nr_cuv<NR && gets(tab_cuv[nr_cuv]))nr_cuv++;
/* la CTRL/Z gets returneaza NULL (= 0) */
/* citirea se poate face si cu scanf:
while(nr_cuv<NR &&
scanf("%s",tab_cuv[nr_cuv])!=EOF)nr_cuv++; */

49
/* daca terminarea se face cu un cuvant vid:
while(nr_cuv<NR &&
strcmp("",gets(tab_cuv[nr_cuv])))nr_cuv++; */
}

/* Varianta: tab_cuv se declara ca tablou de pointeri, se modifica declaratia lui


tab_cuv si functia citire_cuv, restul functiilor raman nemodificate;

#include <alloc.h>
char* tab_cuv[NR];
void citire_cuv(void){
char cuv_crt[LUNG];
printf("*** Se introduc maxim %d cuvinte, terminate cu
CTRL/Z:\n",NR);
while(nr_cuv<NR && gets(cuv_crt)){
if((tab_cuv[nr_cuv]=(char
*)malloc(strlen(cuv_crt)+1))==NULL){
puts("*** Spatiu insuficient ! ***");
exit(1);
}
strcpy(tab_cuv[nr_cuv],cuv_crt);
nr_cuv++;
}
}
*/

void constr_cuv(void){
char c,cuv[NR+1]; /* lungimea maxima este egala cu numarul
maxim de cuvinte + 1 pentru terminator */
int i,j,lung; /* i parcurge cuvintele din tab_cuv, j
pozitiile din cuv, lung e lungimea cuv curent */
for(i=j=0;i<nr_cuv;i++)
if((lung=strlen(tab_cuv[i]))>0 &&
isalpha(c=tolower(tab_cuv[i][lung-1])))
cuv[j++]=c;
cuv[j]='\0';
printf("*** Cuvantul construit:|%s|\n",cuv);
}

void cuv_max(void){
int i,lung_crt,lung_max=0;
char * p_max; /* pointerul spre cuvantul maxim */
/* se poate memora indicele cuvantului maxim: int i_max;
sau memora cuvantul maxim intr-un sir: char
c_max[LUNG]; */
for(i=0;i<nr_cuv;i++)
if((lung_crt=strlen(tab_cuv[i]))>lung_max){
p_max=tab_cuv[i];
lung_max=lung_crt;
}
printf("*** Cuvantul de lungime maxima %d
este:|%s|\n",lung_max,p_max);
}

50
void cuv_vocale(void){
int i;
puts("*** Cuvintele ce incep cu vocale:");
for(i=0;i<nr_cuv;i++)
switch(toupper(tab_cuv[i][0])){
case 'A': case'E': case 'I': case 'O': case
'U':puts(tab_cuv[i]);
} /* in loc de switch se putea folosi
char c; if(c=toupper(tab_cuv[i][0]),c=='A' ||
c=='E' || ...)puts(tab_cuv[i]); */
}

void main(void){

citire_cuv();
cuv_max();
constr_cuv();
cuv_vocale();

getch();
}

Se citesc trei siruri s1, s2 si s3. Sa se afiseze sirul obtinut prin inlocuirea in s1 a
tuturor aparitiilor lui s2 prin s3. (Observatie: Daca s3 este sirul vid, din s1 se vor
sterge toate subsirurile s2).

#include <stdio.h>
#include <string.h>

#define LUNGS 81

void main(void){
char s1[LUNGS],s2[LUNGS],s3[LUNGS],rezultat[LUNGS];
char *s1ptr=s1,*s2pos, *rezptr=rezultat;

puts("*** sirul s1:"); gets(s1);


puts("*** subsirul s2:"); gets(s2);
puts("*** s3:"); gets(s3);

while(s2pos=strstr(s1ptr,s2)){
while(s1ptr<s2pos)*rezptr++=*s1ptr++;
strcpy(rezptr,s3);
rezptr+=strlen(s3);
s1ptr+=strlen(s2);
}
strcpy(rezptr,s1ptr);
puts("*** sirul rezultat:"); puts(rezultat);

getch();
}

51
4. Pornind de la un sir citit de la tastatura, sa se construiasca si sa se tipareasca

• sirul care contine cifrele din cel initial


• sirul care contine minusculele vocalelor din cel initial
• sirul invers celui initial; sa se verifice daca sirul initial este palindrom.

#include <stdio.h>
#include <conio.h>
#include <string.h>
#include <ctype.h>

#define LUNGS 81

//variabile globale
char sir[LUNGS],aux[LUNGS];//sirul citit si cel in care se fac
prelucrarile

//prototipuri
void constr1(void);
void constr2(void);
void constr3(void);

//definire main
void main(void){

clrscr();
puts("*** sirul de prelucrat:"); gets(sir);

constr1();
constr2();
constr3();

getch();
}//main

//definire functii
void constr1(void){
int i,j; //i indice in sir, j in aux
for (i=j=0;i<strlen(sir);i++) // expresia conditie poate fi
sir[i]
if(isdigit(sir[i])) // conditia echivalenta
cu '0'<=sir[i]<='9'
aux[j++]=sir[i];
aux[j]='\0';
printf("Sirul de cifre:%s\n",aux);
}

void constr2(void){
int i,j;
char c;
for (i=j=0;i<strlen(sir);i++)
if(c=tolower(sir[i]),c=='a'||c=='e'||c=='i'||c=='o'||c=='u
')
aux[j++]=c;

52
aux[j]='\0';
printf("Sirul de vocale:%s\n",aux);
}

void constr3(void){
int i,lung; //lung ia valoarea lungimii sirului
for (i=0,lung=strlen(sir);i<lung;i++)
aux[i]=sir[lung-1-i];
aux[i]='\0';
printf("Sirul invers:%s\n",aux);

//verificare daca sirul initial este palindrom <=> identic


cu inversul sau
if(strcmp(sir,aux)==0)
puts("*** Este palindrom");
else
puts("*** Nu este palindrom");
}

XI. Fișiere (în C/C++)

1. Operaţii de intrare / ieşire.


In limbajul C nu există instrucţiuni de intrare/ieşire. Operaţiile de intrare / ieşire sunt
realizate prin apelul unor funcţii ale sistemului de operare. Acestea sunt implementate prin
funcţii, sub o formă compatibilă pentru diversele sisteme de operare (sunt portabile).

Un fişier este o colecţie ordonată de articole (înregistrări) păstrate pe un suport extern


de memorie şi identificate printr-un nume.

Pentru fişierul standard de intrare , datele sunt introduse de la tastatură.

Pentru fişierul standard de ieşire , rezultatele sunt afişate pe terminalul standard de


ieşire.

Mesajele de eroare se afişează în fişierul standard de eroare.

Fişierul are un articol care marchează sfârşitul fişierului. Pentru fişierul standard de
intrare de la tastatură, sfârşitul de fişier, pentru sistemele de operare DOS şi Windows se
generează prin Ctrl-Z (pentru Unix – prin Ctrl-D). Vezi si EOF

Operaţiile specifice prelucrării fişierelor sunt.

• deschiderea unui fişier


• închiderea unui fişier
• crearea unui fişier
• citirea de articole din fişier (consultarea fişierului)
• actualizarea (sau modificarea) fişierului
• adăugare de articole la sfârşitul fişierului
53
• poziţionarea în fişier.
• ştergerea unui fişier
• schimbarea numelui unui fişier

Prelucrarea fişierelor se face pe două niveluri:

nivelul inferior, care apelează direct la sistemul de operare.

nivelul superior, care utilizează structuri speciale FILE (structuri de date, FILE *pf; )

Funcţiile de pe nivelul superior nu asigură o independenţă totală faţă de sistemul de operare.

Funcţiile standard de intrare/ieşire au prototipurile în fişierul antet <stdio.h>.

2. Fişiere text şi fişiere binare

Într-un fişier text, toate datele sunt memorate ca şiruri de caractere, organizate pe linii,
separate între ele prin marcajul sfârşit de linie ‘\n’. (EOL=<E>: <CR>+<LF=N>)

Într-un fişier text spaţiul de memorare pe disc nu este folosit în mod eficient pentru
datele numerice (astfel întregul 12345 ocupă 5 octeţi).

Într-un fişier binar, datele sunt păstrate în formatul lor intern (2 octeţi pentru int, 4
octeţi pentru float, etc).

La fişierele text marcajul de sfârşit de fişier (caracterul 0X1A) există fizic în fişier. La
întâlnirea acestui caracter funcţia fgetc() întoarce EOF (-1). Marcajul de sfârşit de fişier
se generează de la tastatură prin Ctrl-Z.

În cazul fişierelor binare, marcajul de sfârşit de fişier nu există fizic în fişier, ci este generat
de funcţia fgetc().

În MSDOS (şi în Unix), la nivelul liniei de comandă intrările şi ieşirile standard pot fi
redirectate în fişiere disc, fără a opera nici o modificare la nivelul programului. Astfel:

< redirectează intrarea standard către fişierul specificat

> redirectează ieşirea standard către fişierul specificat

Fişierul standard de eroare nu poate fi redirectat.

Fişierul specificat poate fi:

con – pentru consola sistem (tastatura, respectiv ecranul)

prn – pentru imprimanta paralelă

com1 – pentru interfaţa serială de date

nume_fişier – pentru un fişier disc

cin>>fisier;

54
NUL – pentru perifericul nul.

Exemple:

> test.exe > prn redirectează ieşirea programului la imprimantă

> test.exe < f1.dat > f2.dat redirectează atât intrarea cât şi ieşirea
programului

3. Accesul la fişiere.

Fişierele disc şi fişierele standard sunt gestionate prin pointeri la structuri


specializate FILE, care se asociază fiecărui fişier pe durata prelucrării.

Fişierele standard au pointerii predefiniţi: stdin, stdout, stderr, stdprn,


stdaux.

Declararea unui pointer la fişier se face prin:

FILE *pf;

deschiderea unui fişier:

Înainte de a fi prelucrat, un fişier trebuie să fie deschis. Prin deschidere:

se asociază unui nume de fişier un pointer la fişier

se stabileşte un mod de acces la fişier

Pentru deschiderea unui fişier se foloseşte funcţia cu prototipul:

FILE *fopen(char *nume_fisier, char *mod_acces);

Prin deschiderea unui fişier se stabileşte o conexiune logică între fişier şi variabila pointer şi
se alocă o zonă de memorie (buffer) pentru realizarea mai eficientă a operaţiilor de intrare /
ieşire. Funcţia întoarce:

un pointer la fişier, în caz că deschiderea fişierului se face în mod corect

NULL dacă fişierul nu poate fi deschis.

Vom considera mai întâi două moduri de acces :

citire (sau consultare) “r” – citirea dintr-un fişier inexistent va genera eroare

scriere (sau creare) “w” - dacă fişierul există deja, el va fi şters=suprascris

Fişierele standard nu trebuiesc deschise.

Redirectarea unui fişier deschis poate fi realizată cu:

FILE* freopen(char* nume, char* mod, FILE* flux));

55
// flux=fisier=buffer

Fişierul deschis este închis şi este deschis un nou fişier având ca sursă fluxul, numele şi
modul de acces specificaţi ca parametri. O utilizare importantă o constituie redirectarea
fluxului standard de intrare: datele vor fi citite din fişierul specificat, fără a face nici o
modificare în program.

fisier>>citesc_ceva; fisier<<scriu_ceva

închiderea unui fişier

După terminarea prelucrărilor asupra unui fişier, acesta trebuie închis. Un fişier este închis
automat la apelarea funcţiei exit().

int fclose(FILE *pf);

funcţia întoarce 0 la închidere normală şi EOF la producerea unui incident

fişierele standard nu se închid de către programator

în cazul unui fişier de ieşire, se scriu datele rămase nescrise din buffer în fişier, aşa că
operaţia de închidere este obligatorie

în cazul unui fişier de intrare, datele necitite din bufferul de intrare sunt abandonate

se eliberează bufferele alocate

se întrerupe conexiunea pointer – fişier

Secvenţa următoare deschide un fişier cu un nume dat şi apoi îl închide.

FILE *pf = fopen(“test1.dat”, ”w”); //set de prelucrari

fclose(pf);

4. Operaţii de intrare – ieşire.

Tip fişier Conversie Unitate Funcţii folosite


transferată

Fără Caracter fgetc(), fputc()

Text Linie fgets(), fputs()

Cu Linii fscanf(). fprintf()

binar Fără Articol (structură) fread(), fwrite()

4.1. operaţii de intrare / ieşire la nivel de caracter

Scrierea unui caracter într-un fişier se face folosind funcţia:

int fputc(int c, FILE *pf);


56
Funcţia întoarce primul parametru sau EOF, în caz de eroare.

Citirea unui caracter dintr-un fişier se face cu funcţia:

int fgetc(FILE *pf);

Funcţia întoarce ca rezultat următorul caracter citit din fişier, convertit în întreg fără
semn sau EOF dacă s-a citit sfârşit de fişier sau s-a produs o eroare la citire.

Scrieţi un program care realizează copierea unui fişier. Numele celor doua fişiere (sursă şi
destinaţie) sunt citite de la terminal.

#include <stdio.h>

/* copierea unui fisier */

void copiere1(FILE *, FILE *);

void main(void){

char numes[12], numed[12];

gets(numes);

gets(numed);

FILE* s = fopen(numes,”r”);

FILE* d = fopen(numed,”w”);

copiere1(d, s);

fclose(s);

fclose(d);

void copiere1(FILE *d, FILE *s) {

int c;

while ((c=fgetc(s)) != EOF)

fputc(c, d);

operaţii de intrare / ieşire pentru şiruri de caractere

char *fgets(char *s, int n, FILE *pf);

citeşte caractere din fişierul cu pointerul pf, până la întâlnirea primului caracter ‘\n’ (cel
mult n-1 caractere) în tabloul s; pune la sfârşit ‘\n’ şi ‘\0’

întoarce s sau NULL la întâlnire sfârşit de fişier sau la eroare

57
Scrieţi o funcţie care simulează funcţia fgets().

char *fgets(char *s, int n, FILE *pf) {

char c;

char *psir = s;

while (--n > 0 &&(c=fgetc(pf))!=EOF) //n citesc pana la n


car

if((*psir++=c)==’\n’)

break;

*psir=’\0’;

return (c==EOF && psir==s) ? NULL: s;

int fputs(char *s, FILE *pf);

copiază şirul în fişierul de ieşire

nu copiază terminatorul de şir ‘\0’

întoarce un rezultat nenegativ (numărul de caractere scrise în fişier), sau EOF la producerea
unei erori

Scrieţi o funcţie care simulează funcţia fputs().

int fputs(char *s, FILE *pf)

{ int c, n=0;

while (c = *s++){

fputc(c, pf);

n++;

return (ferror(pf)) ? EOF: n;

Copierea unui fişier folosind funcţii orientate pe şiruri de caractere are forma:
58
#define MAX 100

void copiere2(FILE *d, FILE *s){

char linie[MAX];

while(fgets(linie, MAX, s))

fputs(linie, d);

Revenim acum asupra modurilor de acces la disc. Sunt posibile următoarele situaţii:

fişierul nu există; dorim să-l creem şi să punem informaţii în el

“w” - deschidere pentru scriere, noile scrieri se fac peste cele vechi

fişierul există deja; dorim să extragem informaţii din el

“r” - deschidere pentru citire, fişierul trebuie să existe deja

– “r+” - citire şi scriere ; fişierul trebuie să existe

fişierul există deja; dorim să adăugăm informaţii la el, păstrând informaţiile deja existente

– “a” - deschidere pentru adăugare, toate scrierile se adaugă la sfârşitul fişierului


existent sau nou creat

“a+” - citire şi adăugare; dacă fişierul nu există, el va fi creat

fişierul există deja; dorim să punem alte informaţii în el ştergând pe cele existente

– “w+” - citire şi scriere; dacă fişierul există deja el este şters

Modul de acces binar se specifică cu sufixul “b”. Astfel avem: “rb”, “w+b”

Modul text este considerat implicit, dar poate fi specificat explicit prin “t”.

4.3. operaţii de intrare / ieşire cu format

scrierea cu format

int fprintf(FILE *pf, char *format, lista_expresii);

transferă în fişierul specificat, valorile expresiilor, convertite, potrivit formatului în caractere

întoarce numărul de caractere scrise, sau o valoare negativă, dacă s-a produs o eroare.

Un descriptor de conversie din format începe prin % şi poate avea un mai mulţi specificatori
opţionali, care preced descriptorul:

%[indicator][lăţime][.precizie][spec_lung]descriptor;

Indicatorul poate avea una din valorile:


59
- aliniere stânga

+ afişare numere cu semn

completare stânga cu zerouri

adaugare spaţiu înaintea primei cifre, dacă numărul este pozitiv

# %#o - scrie 0 iniţial

%#x - scrie 0x

%#e,f,g,E,G – scrie punctul zecimal şi nu elimină zerourile la sfârşit

Lăţimea – număr ce indică lăţimea minimă a câmpului în care se face scrierea.

* lăţimea este dată de argumentul următor

Precizia este un număr interpretat diferit în funcţie de descriptorul folosit.Astfel:

%e, %E, %f – numărul de cifre după punctul zecimal

%s –numărul maxim de caractere afişate

%g, %G – numărul de cifre semnificative

%d, %i – numărul minim de cifre (cu zerouri în faţă)

Specificarea lungimii se face prin:

H – short l – long L – long double

citirea cu format

int fscanf(FILE *pf, char *format, lista_adrese_variabile);

se citesc date din fişierul pf, sub controlul formatului, iniţializându-se variabilele din
listă

funcţia întoarce numărul de câmpuri citite sau EOF în caz de producere a unui
incident la citire sau întâlnire a marcajului de sfârşit de fişier.

4.4. intrări / ieşiri în modul de acces binar

sunt operaţii de transfer (citiri / scrieri) fără conversii

se fac la nivel de articol

poziţia în fişier este actualizată după fiecare citire / scriere

unsigned fread(void *zona, unsigned la, unsigned na, FILE


*pf);
60
citeşte cel mult na articole, de lungime la fiecare, din fişierul pf în zona

întoarce numărul de înregistrări citite sau 0 în caz de eroare sau sfârşit de fişier

unsigned fwrite(void *zona, unsigned la, unsigned na, FILE *pf);

scrie na articole de lungime la, din zona în fişierul pf

întoarce numărul de articole scrise.

Pentru a copia un fişier binar (sau text) folosind funcţiile fread() şi fwrite() vom
considera lungimea articolului 1 octet.

void copiere3(FILE * d, FILE * s) {

int noc; /* numarul de octeti cititi */

char zona[MAX];

while((noc=fread(zona, 1, MAX, s)) > 0)

fwrite(zona, 1, noc, d);

5. Operaţii pentru fişiere cu acces direct.

5.1. Poziţionarea în fişier

int fseek(FILE *pf, long depl, int orig);

modifică poziţia curentă în fişierul cu pointerul pf cu depl octeţi relativ la cel de-al
treilea parametru orig, după cum urmează:

faţă de începutul fişierului, dacă orig=0 (sau SEEK_SET)

faţă de poziţia curentă, dacă orig=1 (sau SEEK_CUR)

faţă de sfârşitul fişierului, dacă orig=2 (sau SEEK_END)

întoarce rezultatul 0 pentru o poziţionare corectă, şi diferit de 0 în caz de eroare.

void rewind(FILE *pf);

realizează o poziţionare la începutul fişierului, fiind echivalent cu:

fseek(pf, 0L, SEEK_SET);

long ftell(FILE *pf);

întoarce poziţia curentă în fişier, exprimată prin numărul de octeţi faţă de începutul
fişierului

Scrieţi o funcţie care determină numărul de octeţi ai unui fişier.

61
#include <stdio.h>

long FileSize(FILE *pf)

{ long pozv, noct;

pozv = ftell(pf); /* salveaza pozitia curenta */

fseek(pf, 0L, SEEK_END); /* pozitionare la sfarsit */

noct = ftell(pf); /* numar de octeti din fisier */

fseek(pf, pozv, SEEK_SET); /*revenirea la pozitia veche*/

return noct;

Tratarea erorilor

int feof(FILE *pf);

întoarce o valoare diferită de 0, dacă s-a detectat marcajul de sfârşit de fişier

int ferror(FILE *pf);

întoarce o valoare diferită de 0, dacă s-a detectat o eroare în cursul operaţiei de


intrare / ieşire

Fişierul “comenzi.dat” conţine articole structuri cu câmpurile:

-denumire produs- un şir de 20 de caractere

-cantitate comandată – o valoare reală.

Fişierul “depozit.dat” este format din articole având câmpurile:

- denumire produs – un şir de 20 de caractere

- stoc şi stoc_minim– valori reale

- preţ unitar – valoare reală

Să se actualizeze fişierul de stocuri, prin onorarea comenzilor. O comandă este


onorată, dacă prin satisfacerea ei, stocul rămas în magazie nu scade sub stocul
minim.

Se va creea un fişier “facturi.dat”, conţinând pentru fiecare comandă onorată,


denumirea produsului comandat şi valoarea comenzii.

Se vor crea de asemeni două fişiere, unul cu comenzi care nu au fost satisfăcute,
deoarece produsele erau în cantităţi insuficiente, celălalt cu comenzi de produse care
nu există în depozit.
62
#include <stdio.h>

#include <stdlib.h>

typedef struct { char den[20];

double cant;} comanda;

typedef struct { char den[20];

double stoc, stoc_min, pret;} depozit;

typedef struct { char den[20];

double val;} factura;

void main(void)

{ comanda com;

depozit dep;

factura fac;

double t;

int gasit, eof;

FILE *fc, *fd, *ff, *fc1, *fc2;

/* deschidere fisiere */

if((fc=fopen(“comenzi.dat”,”rb”))==NULL){

fprintf(stderr,”eroare deschidere comenzi\n”);

exit(1);

};

if((fd=fopen(“depozit.dat”,”r+b”))==NULL){

fprintf(stderr,”eroare deschidere depozit\n”);

exit(1);

};

ff=fopen(“facturi.dat”,”wb”);

fc1=fopen(“com1.dat”,”wb”);

fc2=fopen(“com2.dat”,”wb”);

while(1) { /* ciclu procesare comenzi */

63
if(!fread(&com, sizeof(com), 1, fc) break;

gasit=0;

rewind(fd);

do {

eof=fread(&dep, sizeof(dep), 1, fd)==0;

if(!eof) {

if(strcmp(com.den, dep.den)==0){

gasit = 1;

if((t=dep.stoc – com.cant)>=dep.stoc_min) {

dep.stoc=t;

fseek(fd, -sizeof(dep), SEEK_CUR);

fwrite(&dep, sizeof(dep), 1, fd);

fac.val=com.cant * dep.pret;

strcpy(fac.den, com.den);

fwrite(&fac, sizeof(fac), 1, ff);

else

fwrite(&com, sizeof(com), 1, fc1);

break;

} while(!gasit && !eof);

if(!gasit)

fwrite(&com, sizeof(com), 1, fc2);

fclose(fc);

fclose(fd);

fclose(ff);

fclose(fc1);

64
fclose(fc2);

Semnătură Efect

FILE* fopen(char* nume, char* mod); deschide fişierul; întoarce pointerul la


fişierul deschis sau NULL dacă
operaţia eşuează

int fclose(FILE* pf); închide fişierul

int fgetc(FILE* pf); citeşte 1 caracter din fişierş întoarce


caracterul citit sau EOF

int fputc(char c, FILE* pf); scrie caracterul în fişier

char* fgets(char* s, int n, citeşte din fişier în s cel mult n-1


caractere,sau până la întâlnire ‘\n’, în
FILE* pf); locul căruia pune ‘\0’. Întoarce s sau
NULL, dacă s-a citit EOF.

int fputs(char* s, FILE* pf); copiază şirul în fişierul de


ieşire.Înlocuieşte terminatorul ‘\0’ cu
‘\n’.

int fscanf(FILE* pf, char* format, citire din fişier sub controlul
formatului. Întoarce numărul de
lista_adrese); câmpuri citite sau EOF, în caz de
eroare sau sfârşit de fişier.

int fprintf(FILE* pf, char* format, scriere în fişier sub controlul


formatului. Întoarce numărul de
lista_expresii); caractere scrise sau valoare negativă
în caz de eroare.

int fread(char* zona, int la, citeşte din fişier în zona, na articole
de lungime la fiecare.Întoarce
int na, FILE* pf); numărul de articole efectiv citite.

int fwrite(char* zona, int la, scrie în fişier din zona, na articole de
lungime la fiecare.Întoarce numărul
int na, FILE* pf); de articole efectiv scrise.

int fseek(FILE* pf, long depl, poziţionare cu depl octeţi faţă de


început, poziţia curentă sau sfârşitul
int orig); fişierului

void rewind(FILE* pf); poziţionare la începutul fişierului

65
long ftell(FILE* pf); determină poziţia curentă în fişier.

int feof(FILE* pf); întoarce nenul dacă s-a detectat


sfârşit de fişier

int ferror(FILE* pf); întoarce nenul dacă s-a detectat o


eroare în cursul operaţiei de intrare /
ieşire

Fişiere în C++
FILE *pf=open(„cale://nume”...);// in C

Realizarea funcţiilor de intrare/ieşire în C++ se realizează prin intermediul fluxurilor


(streamuri). Un flux este o clasă de obiecte (date si metode).

Fluxurile pot fi clasificate în:

fluxuri de intrare/ieşire standard

fluxuri de intrare/ieşire folosind fişiere

fluxuri de intrare/ieşire în memorie

În instrucţiunea cout << x; cout este un obiect de tip flux de ieşire standard
(monitorul). Transferul informaţiei în fişier se realizează prin intermediul operatorului <<
supraîncărcat (operator pe biti, deplasare stanga: 111(7)->11(3)).

cin este obiectul flux de intrare standard; transferul informaţiei se face prin operatorul
supraîncărcat >>.

Obiectele cin şi cout sunt declarate în iostream.h.

Utilizatorul îşi defineşte propriile fluxuri, declarându-le ca obiecte în clasele:

ofstream., pentru operaţii de scriere (of=open file)

ifstream, pentru operaţii de citire (if=input file)

fstream, pentru operaţii atât de citire, cît şi de scriere

Pentru a folosi aceste clase trebuie inclus fişierul antet fstream.h .

Operații cu fluxuri

Deschiderea unui fişier (asocierea fişier – stream) se face cu funcţia membru open(), avînd
semnătura:

66
void open(char* numefisier, int mod, int acces);

Modul în care poate fi deschis mod un fişier este precizat în clasa ios prin
enumeratorii:

in - fişierul se deschide pentru citire; fişierul trebuie să existe (vezi exemplul de


mai jos) ios::in

out - fişierul se deschide pentru scriere; dacă există – se şterge şi se crează un


nou fişier ios::out

ate - fişierul se deschide pentru adăugare la sfârşit, dacă nu există – se crează

app - fişierul se deschide pentru adăugare la sfârşit; fişierul trebuie să existe

trunc - dacă fişierul există, va fi şters şi se va crea un fişier nou pentru scriere

nocreate - fişierul deschis trebuie să existe (el nu poate fi creat)

noreplace - fişierul este deschis, iar conţinutul lui nu poate fi înlocuit

binary - fişerul deschis va fi prelucrat ca un fişier binar (pt fisiere cu continut altul
decat text)

ifstream f;

f.open(”note.in”, ios::in); // extensii permise .txt .in .out

Modul de acces acces poate specifica una din valorile:

0 – fişier fără restricţii de acces

1 – fişier protejat la scriere

2 – fişier ascuns

4 – fişier sistem

8 – fişier arhivă

Desfacerea legăturii fişier – stream se face folosind funcţia close().

Obs. Dacă în urma deschiderii fişierului, variabila stream este egală cu NULL, atunci
operaţia de deschidere a eşuat.

ifstream f;

if(!f.open(”note.dat”, ios::in)){

cerr << ”eroare la deschidere!\n”; // cerr console error

exit(-1);

67
}

Obs. Deschiderea fişierului se poate face implicit, fără a folosi funcţia open(),
dacă parametrii sunt specificaţi la declararea obiectului fişier:

ifstream f(“note.dat”, ios::in);

Operatorii de inserţie (<<) şi extracţie (>>) sunt supraîncărcaţi, putând fi folosiţi


pentru a scrie, respectiv citi din fişier.

Metode (functii de prelucrare) specifice:

Semnătura Efect

void open(const char* nume, deschide fişierul numit, în modul specificat,


cu restricţiile de acces precizate
int mod, int acces);

void close(); închide fişierul

istream& get(char& c); citeşte un caracter din fluxul de intrare; la


citire EOF întoarce NULL

int peek(); preia următorul caracter din fluxul de


intrare, fără a-l extrage

istream& putback(); pune înapoi în fişier un caracter

istream& get(char* sir, int n, se citesc cel mult n-1 caractere, sau până
la întâlnirea separatorului sep. Separatorul
char sep=’\n’);
este pus în şir.

istream& getline(char* sir, se citesc cel mult n-1 caractere, sau până
la întâlnirea separatorului sep. În locul
int n, char
separatorului este pus terminatorul de şir
sep=’\n’);
‘\0’.

istream& read(char* sir, int citeşte în mod binar cel mult n caractere
n);

int gcount(); determină câte caractere s-au citit în ultima


operaţie de citire

long tellg(); determină poziţia curentă la citire

istream& seekg(long deplas, poziţionare cu deplas octeţi fată de:


început (ios::beg), poziţia curentă
int
(ios::cur) sau sfârşit (ios::end)
orig=ios::beg);

ostream& put(char c); inserează caracterul c în fluxul de ieşire

68
ostream& write(const char* scrie cel mult n caractere în fluxul de ieşire
sir,

int n);

long tellp(); determină poziţia curentă la scriere

ostream& seekp(long deplas, poziţionare cu deplas octeţi fată de:


început (ios::beg), poziţia curentă
int
(ios::cur) sau sfârşit (ios::end)
orig=ios::beg);

Exemplu:

#include <fstream.h>

void main(){

ofstream out;

ifstream in;

out.open(“iesire.dat”);

out << “Vasilica invata C++” << “\n”;

out.close();

in.open(“intrare.dat”);

char linie[80];// in mod grafic ecranul are o lung de 80 car

in >> linie;

in.close();

cout << linie;

O alternativă la teorie poate fi resursa [9], pentru lucrul cu fișiere în C.

Cele două aplicații din resursa 9, le aveți mai jos:

1. Prelucrarea fișierului pe caractere și șiruri de caractere

#include <stdio.h>
#include <conio.h>
int main()
{
char ch, s[100], nume_fis[50]="D:\\fisier1.txt";
int i;
69
FILE *pf;
// crearea fişierului; scrierea caracterelor inclusiv '\n'
introduse de la tastatura
pf=fopen(nume_fis,"w");
printf("Introduceti textul. Pentru a termina apasati CTRL+Z si
ENTER \n");
while ((ch=getc(stdin))!=EOF)
{
putc(ch,pf);
}
fclose(pf);
/*Adaugarea de siruri de caractere*/
pf=fopen(nume_fis,"r+");
fseek(pf,0l,SEEK_END);
printf("Introduceti sirurile de caractere terminate cu ENTER.
Pentru a termina\
apasati CTRL+Z si ENTER\n");
while(fgets(s,100,stdin)!=(char*)0)
{
fputs(s,pf);
}
fclose(pf);
/*Afisarea continutului */
printf("CONTINUTUL FISIERULUI cu NUMEROTAREA LINIILOR:\n");
i=0;
pf=fopen(nume_fis,"r");
while(fgets(s,100,pf)!=(char *)0)
{
printf("%d %s", i, s);
i++;
}
fclose(pf);
printf("Dupa apasarea unei taste fisierul va fi sters!\n");
getch();
remove(nume_fis);
return 0;
}

2. Prelucrare binară a unui fişier. Programul conţine crearea fişierului şi afişarea


conţinutului său

#include <stdio.h>
/* Programul ilustreaza prelucrarea binara a unui fisier */
typedef struct {
char nume[40];
long suma;
} ARTICOL;

void afisare(char *nume_fis) {


FILE *pf;
ARTICOL buf;
int i;
pf=fopen(nume_fis,"rb");
printf("NR.CRT. SUMA NUMELE-PRENUMELE\n");
i=0;
while(fread(&buf,sizeof(ARTICOL),1,pf)>0)
{
70
printf("%6d %10ld %-40s\n",i,buf.suma,buf.nume);
i++;
}
fclose(pf);
}

int main()
{
FILE *pf;
ARTICOL buf;
int i,n;
char nume_fis[40]="C:\\fisier2.dat";
/*Crearea fisierului */
printf("Introduceti numarul de persoane: ");
scanf("%d",&n);
pf=fopen(nume_fis,"wb");
for(i=1;i<=n;i++)
{
fflush(stdin);
printf("Numele persoanei: ");
fgets(buf.nume,40,stdin);
printf("Suma = ");
scanf("%ld",&buf.suma);
fwrite(&buf,sizeof(ARTICOL),1,pf);
}
fclose(pf);
printf("\nCONTINUTUL FISIERULUI\n");
afisare(nume_fis);
return 0;
}

Exemple de subiecte de examen

Nr. 1.
I. Structuri de control fundamentale
II. Metoda bulelor (secvența de cod)
III. Scrieți o funcție recursivă pentru calculul sumei unei progresii aritmetice
IV. Scrieti codul prin care un fișier text sursa.dat este copiat în invers.dat, în
ordine inversă a liniilor

Nr. 2
I. Structuri de control derivate
II. Sortare cu pivotare
III. Scrieți o funcție recursivă pentru calculul sumei unei progresii geometrice
IV. Scrieti codul prin care un fișier text sursa.dat este copiat în invers.dat,
fiecare linie din primul regăsindu-se în ordine inversă în cel de al doilea.

71
Resurse disponibile online

[1]. https://ro.scribd.com/document/366479012/02-Algoritmi

[2]. http://users.utcluj.ro/~igiosan/Resources/PC/Lab/L01.pdf

[3]. http://www.scritub.com/stiinta/informatica/Etapele-realizarii-unui-
progra184261513.php

[4]. http://www.asciitable.com

[5]. http://www.cplusplus.com/doc/tutorial/operators/

[6]. http://thor.info.uaic.ro/~pc/pcII/stilC++/index.html#top

[7]. http://infoscience.3x.ro/c++/recursivitate.htm

[8]. https://info64.ro/Recursivitate/

[9]. http://users.utcluj.ro/~igiosan/Resources/PC/Lab/L11.pdf

Bibliografie

1. Andone R., Gârbacea I., Algoritmi fundamentali o perspectivă C++, Editura Libris,
Cluj Napoca, 1995.
2. Calude C., Complexitatea calcului, aspecte calitative, Editura Ştiinţifică şi
Enciclopedică, Bucureşti, 1982.
3. Cormen T.H., Leiserson E.C., Rivest R.R., Introducere în algoritmi, Editura Libris
Agora, 2000 (traducere în limba română).
4. Dahl O.J., Dijkstra E.W., Hoare C.A.R., Structured Programing, Academic Press,
1972.
5. Frenţiu M., Motogna S., Lazăr I., Prejmerean V., Elaborarea algoritmilor, Litografia
Universităţii “Babeş Bolyai”, Cluj Napoca, 1998.
6. Knuth E. Donald, Arta programării calculatoarelor, orice editie.
7. Lica D., Onea E., Informatică, Manual pentru clasa a IX-a, Editura L&S, Bucureşti,
1999.
8. Livovschi L., Georgescu H., Sinteza şi analiza algoritmilor, Editura Ştiinţifică şi
Enciclopedică, Bucureşti, 1986.
9. E. Horowitz, S. Sahni, "Fundamentals of Computer Algorithms" – 1985 ca şi
"Fundamentals of Data Structures".
10. McConnell S.C., Code Complete: a practical handbook of software construction,
Microsoft Press, Washington, 1993
11. Motogna S.,Metode şi tehnici de proiectare a algoritmilor, Universitatea “Babeş
Bolyai”, Cluj Napoca, 1998/
12. Tudor S., Cerchez E., Şerban M., Informatică. Manual pentru clasa a IX-a, Editura
L&S, Bucureşti, 1999 (orice manual de liceu de C/C++ e util)
13. R. Vişinescu, V. Vişinescu- Algoritmi şi structuri de date- Teorie şi aplicaţii.
Probleme de concurs, Editura Edusoft, 2006.

72

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