PCLP

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

Sunteți pe pagina 1din 310

See discussions, stats, and author profiles for this publication at: https://www.researchgate.

net/publication/341882834

Florin POSTOLACHE Programarea calculatoarelor şi limbaje de programare


prin exemple practice

Book · June 2018

CITATIONS READS

0 6,309

1 author:

Florin Postolache
Mircea cel Batran Naval Academy
29 PUBLICATIONS 50 CITATIONS

SEE PROFILE

Some of the authors of this publication are also working on these related projects:

e-Learning & ICT Infrastructure Approach View project

Environment and Climate Change View project

All content following this page was uploaded by Florin Postolache on 03 June 2020.

The user has requested enhancement of the downloaded file.


Florin POSTOLACHE

Programarea calculatoarelor şi
limbaje de programare prin
exemple practice
Colecţia „Matematică”
Florin POSTOLACHE

Programarea calculatoarelor şi
limbaje de programare prin
exemple practice

Editura Academiei Navale „Mircea cel Bătrân”


Constanţa, 2018
Referenţi ştiinţifici: Conf. univ. Dr. Florentina-Loredana DRAGOMIR
Conf. univ. Dr. Viorel ARITON

Copyright © 2018 Editura Academiei Navale „Mircea cel Bătrân”


Toate drepturile rezervate

Editare computerizată: Mirela CLEIANU


Copertă: Mirela CLEIANU

Editura Academiei Navale „Mircea cel Bătrân”


Str. Fulgerului nr. 1, 900218, Constanţa
Tel. 0241/626200/174, fax 0241/643096
Email: editura@anmb.ro

ISBN 978-606-642-167-6
Cuprins

BAZELE INFORMATICII ...............................................................................................15


NOȚIUNI INTRODUCTIVE ...................................................................................................15
INFORMAȚII ȘI PRELUCRĂRI ..............................................................................................16
Date .............................................................................................................................16
Informație ....................................................................................................................18
Cunoștințe....................................................................................................................19
Sistemul informațional.................................................................................................19
Sistemul de calcul ........................................................................................................20
REPREZENTAREA ȘI STRUCTURAREA INFORMAȚIEI ......................................22
BIT, OCTET ȘI MULTIPLII ACESTORA ..................................................................................22
TIPURI DE DATE SIMPLE ....................................................................................................24
SISTEME DE NUMERAȚIE ...................................................................................................24
Sistemul de numerație zecimal (baza 10) ....................................................................25
Sistemul de numerație binar (baza 2) ..........................................................................26
Sistemul de numerație octal (baza 8)...........................................................................27
Sistemul de numerație hexazecimal (baza 16) .............................................................27
Interdependența sistemelor de numerație ....................................................................28
Conversia binar – octal - hexazecimal și invers ..........................................................29
Conversia binar – zecimal și invers ............................................................................30
REPREZENTAREA NUMERELOR ÎNTREGI ............................................................................31
PROBLEME PROPUSE: ........................................................................................................33
ALGORITM .......................................................................................................................34
REPREZENTAREA ALGORITMILOR .....................................................................................34
Scheme logice ..............................................................................................................36
Pseudocod ...................................................................................................................37
ELABORAREA ALGORITMILOR ..........................................................................................39
Analiza și complexitatea algoritmilor .........................................................................40
Complexitatea algoritmului .........................................................................................40
PRELUCRAREA INFORMAȚIEI ..................................................................................42
IDENTIFICATOR, VARIABILĂ, CONSTANTĂ, LITERAL .........................................................42
EXPRESII ...........................................................................................................................43
OPERATORI .......................................................................................................................44
TIPURI DE EXPRESII ...........................................................................................................45
INSTRUCȚIUNI ................................................................................................................47
INSTRUCȚIUNI SIMPLE (SECVENȚIALE)..............................................................................48
INSTRUCȚIUNI STRUCTURATE (STRUCTURI DE CONTROL) .................................................49
Instrucțiuni decizionale (de selecție) ...........................................................................50
Algoritmică şi programare

Instrucțiuni repetitive (bucle sau cicluri) ....................................................................53


PROGRAME ȘI SUBPROGRAME .................................................................................56
SUBPROGRAME .................................................................................................................57
PROGRAMUL PRINCIPAL....................................................................................................58
REALIZAREA PROGRAMELOR ȘI PROGRAME SUPORT .........................................................59
PROBLEMATICA PROGRAMĂRII .........................................................................................60
Etape în ciclul de viață ale unui produs program .......................................................61
Limbaje de programare ...............................................................................................68
MEDIUL DE PROGRAMARE RAPTOR .......................................................................88
INTERFAȚA GRAFICĂ UTILIZATOR .....................................................................................88
STRUCTURA UNUI PROGRAM.............................................................................................89
Operații de intrare(input) ............................................................................................91
Operații de prelucrare procesare ................................................................................92
Operații de ieșire (output). ..........................................................................................93
Execuția algoritmului/programului .............................................................................93
SCHEME LOGICE. APLICAȚII PRACTICE UTILIZÂND RAPTOR ...........................................95
Algoritm pentru calculul Sumei a două numere ..........................................................95
Algoritm pentru calculul ariei si perimetrului unui dreptunghi ..................................95
Algoritm pentru rezolvarea ecuației de gradul I: a*x+b=0, unde a și b sunt numere
reale inserate (citite) de la tastatură ...........................................................................97
Algoritm pentru rezolvarea ecuației de gradul 2: ax2+bx+c=0 ..................................97
Algoritm pentru Suma Gauss (suma primelor n numere naturale) .............................99
Algoritm pentru Suma pătratelor/cuburilor primelor n numere naturale .................100
Algoritm pentru calculul 𝒌 = 𝟏𝒏 𝒌𝒌 + 𝟏 .................................................................100
39T

Algoritm pentru calculul 𝒌 = 𝟏𝒏𝒌𝒌 + 𝟏 .................................................................101


39T

Algoritm pentru calculul 𝒌 = 𝟏𝒏𝒌𝟐 + 𝟐𝒌 + 𝟏𝒌𝟐 + 𝟐𝒌 + 𝟐 ..................................101


39T

Schema logică pentru calcului factorialului (n!).......................................................102


Algoritm pentru calculul 𝑪𝒏𝒌 ..................................................................................102
39T

Algoritm pentru calculul 𝑨𝒏𝒌 ..................................................................................103


39T

Algoritm pentru numere prime ..................................................................................103


SCHEME LOGICE. SUBSCHEME SI PROCEDURI ..................................................................104
Schemă logică pentru citirea unui număr natural nenul, determinarea și afișarea
cifrelor sale................................................................................................................105
Schemă logică pentru citirea unui număr natural nenul și afișarea inversului
numărului ..................................................................................................................106
Test palindrom pentru un număr natural nenul, citit de la tastatura ........................106
Mulțimea de palindromuri pana la n .........................................................................107
Mulțimea de palindromuri dintr-un interval .............................................................107
Schema logică pentru citirea unui număr natural nenul, determinarea și afișarea
sumei/produsului cifrelor sale ...................................................................................108
Schema logică pentru afișarea produsului cifrelor unui număr natural, utilizând o
subschemă logică ......................................................................................................108
Schema logică pentru afișarea produsului cifrelor unui număr natural, utilizând o
procedură ..................................................................................................................109

ii
Algoritmică şi programare

Schema logică și pseudocod pentru calculul sumei/produsului cifrelor unui număr


natural nenul utilizând proceduri. .............................................................................109
Aranjamente și combinări utilizând o subschemă logică ..........................................110
Sisteme de numerație. Conversia unui număr din binar (baza 2) în zecimal (baza 10)
...................................................................................................................................110
Conversia unui număr din zecimal (baza 10)în binar (baza 2) .................................111
SCHEME LOGICE. TABLOURI ...........................................................................................111
Algoritm pentru scrierea si citirea elementelor unui vector ......................................111
Algoritm pentru: ........................................................................................................112
Algoritm pentru elementele comune a doi vectori .....................................................113
Algoritm pentru sortarea elementelor vectorului ......................................................113
Matrice. Algoritm (pseudocod si schema logică) pentru scrierea si citirea elementelor
...................................................................................................................................114
Schemă logică și pseudocod pentru citirea/scrierea elementelor matricei, suma
elementelor diagonalei principale si secundare ........................................................114
Algoritm sortare elemente matrice pe linia k ............................................................117
LIMBAJUL DE PROGRAMARE C/C++ .....................................................................118
NOȚIUNI INTRODUCTIVE .................................................................................................118
STRUCTURA UNUI PROGRAM C/C++ ...............................................................................119
UNITĂȚI LEXICALE C/C++ ..............................................................................................120
Mulțimea caracterelor ...............................................................................................120
Comentariul ...............................................................................................................121
Tipuri de date primare...............................................................................................122
Constante ...................................................................................................................124
Constante de tip caracter ..........................................................................................124
Constante de tip șir de caractere ...............................................................................125
Constante de tip întreg ..............................................................................................125
Constante de tip real .................................................................................................125
Cuvinte cheie .............................................................................................................125
Separatori ..................................................................................................................126
Variabile ....................................................................................................................126
EXPRESII. OPERANZI. OPERATORI ..................................................................................127
Expresii......................................................................................................................127
Operanzi ....................................................................................................................127
Conversii implicite de tip...........................................................................................127
Operatori ...................................................................................................................127
Operatori de adresare ...............................................................................................129
Operatori unari .........................................................................................................129
Operatori multiplicativi .............................................................................................130
Operatori aditivi ........................................................................................................131
Operatori pentru deplasare .......................................................................................131
Operatori relaționali .................................................................................................131
Operatori de egalitate ...............................................................................................132
Operatori logici .........................................................................................................132
Operatorul condițional ..............................................................................................132

iii
Algoritmică şi programare

Operatori de atribuire ...............................................................................................132


Operatorul virgulă ....................................................................................................133
STRUCTURI ALGORITMICE. INSTRUCȚIUNI ......................................................134
INSTRUCȚIUNI SIMPLE.....................................................................................................135
INSTRUCȚIUNI COMPUSE .................................................................................................135
Blocul ........................................................................................................................135
Structuri decizionale(ramificate) ...............................................................................136
Structuri repetitive (bucle sau cicluri) .......................................................................139
Descriptori de format ................................................................................................143
APLICAȚII PRACTICE .......................................................................................................143
MASIVE DE DATE .........................................................................................................149
MASIVE DE DATE UNIDIMENSIONALE..............................................................................149
APLICAȚII PRACTICE .......................................................................................................150
Algoritm pentru citirea elementelor unui vector .......................................................150
Algoritm pentru scrierea elementelor unui vector.....................................................150
Algoritm pentru citirea și scrierea elementelor unui vector ......................................150
Suma elementelor unui vector ...................................................................................151
Suma elementelor cu valoare para, produsul elementelor cu valoare impara ale unui
vector .........................................................................................................................152
Sortarea elementelor unui vector in ordine crescătoare ...........................................152
Determinarea valorii minime și poziția sa într-un vector de numere reale...............153
Produsul scalar a doi vectori ....................................................................................154
MASIVE DE DATE BIDIMENSIONALE ................................................................................155
APLICAȚII PRACTICE .......................................................................................................155
Algoritm pentru citirea/scrierea elementelor unei matrice .......................................155
Suma elementelor matricei situate pe diagonala principală si secundară ................156
Suma elemente matrice pe coloana k ........................................................................157
Suma elemente matrice de pe linia k .........................................................................158
MASIVE DE DATE N-DIMENSIONALE................................................................................158
FUNCȚII ALE LIMBAJULUI DE PROGRAMARE C/C++ ......................................160
DECLARAȚII ȘI DEFINIȚII DE FUNCȚII ..............................................................................161
Aplicații practice .......................................................................................................162
ITERAȚIA SI RECURSIVITATEA .........................................................................................166
Aplicații practice .......................................................................................................168
DEFINIREA ȘI TRANSMITEREA PARAMETRILOR. PARAMETRI FORMALI ȘI ACTUALI .........178
ADRESE DE MEMORIE. POINTERI ..........................................................................186
Operatorii de adresare si dereferenţiere ...................................................................189
Pointeri la funcții.......................................................................................................191
Aplicații practice .......................................................................................................192
STRUCTURI ȘI UNIUNI ................................................................................................199
STRUCTURI .....................................................................................................................199
Pointeri către structuri ..............................................................................................203

iv
Algoritmică şi programare

Typedef. Crearea unor noi tipuri de date ..................................................................204


UNIUNI ...........................................................................................................................205
STRUCTURI ȘI UNIUNI. DIFERENȚE ..................................................................................208
DIRECTIVE PRE-PROCESOR .....................................................................................210
MACRO-DEFINIȚII ...........................................................................................................210
Directiva #define .......................................................................................................211
Comparație între macro-definiții și funcții C/C++ ...................................................212
Directiva #undef ........................................................................................................212
Directive de compilare condiționată .........................................................................213
Directivele #if, #else, #elif și #endif ..........................................................................213
Directivele #ifdef şi #ifnde.........................................................................................215
Directiva #include .....................................................................................................216
Directiva #error ........................................................................................................217
FUNCȚII DE INTRARE/IEȘIRE ..................................................................................218
FUNCȚII DE INTRARE/IEȘIRE PENTRU CARACTERE ...........................................................218
FUNCȚII DE INTRARE/IEȘIRE CU FORMAT ........................................................................219
FUNCȚII CU FORMATARE PENTRU ȘIRURI DE CARACTERE................................................220
FUNCȚII PENTRU GESTIUNEA TIMPULUI ..........................................................................220
Aplicații practice: ......................................................................................................223
FUNCȚII PENTRU OPERAȚII MATEMATICE ........................................................................226
Constante simbolice...................................................................................................226
Funcții trigonometrice ...............................................................................................226
Funcții putere și radical ............................................................................................227
Funcții exponențiale, logaritmice și hiperbolice .......................................................227
Funcții de conversie ..................................................................................................228
Funcții parte întreagă, de rotunjire și de trunchiere .................................................229
Funcții modul ............................................................................................................229
Funcții pentru generarea numerelor aleatoare .........................................................229
Aplicații practice .......................................................................................................230
FUNCȚII PENTRU OPERAȚII CU ȘIRURI DE CARACTERE .....................................................232
Aplicații practice .......................................................................................................236
FUNCȚII PENTRU CLASIFICAREA CARACTERELOR ...........................................................245
Aplicații practice .......................................................................................................247
ALOCAREA DINAMICĂ A MEMORIEI ....................................................................254
Funcțiile standard pentru gestiunea dinamică a memoriei .......................................256
Alocarea dinamică a memoriei..................................................................................257
Alocarea dinamică a memoriei utilizând pointeri .....................................................258
Aplicații practice .......................................................................................................258
Funcții pentru operații cu blocuri de memorie..........................................................264
Aplicații practice .......................................................................................................264
FIȘIERE I/O. FUNCȚII PENTRU OPERAȚII ASUPRA FIȘIERELOR ..................272
FIȘIERE DE DATE. INTRODUCERE ....................................................................................272
DESCHIDEREA ȘI/SAU CREAREA UNUI FIȘIER ...................................................................274

v
Algoritmică şi programare

Deschiderea și/sau crearea unui fișier text ...............................................................275


Deschiderea și/sau crearea unui fișier binar ............................................................275
Poziționarea indicatorului de citire/scriere ..............................................................279
Funcții pentru tratarea erorilor ................................................................................281
Funcții de citire/scriere fără format ..........................................................................282
Funcții de citire/scriere șir de caractere ...................................................................284
Aplicații practice .......................................................................................................285
METODE DE PROGRAMARE .....................................................................................288
METODA “DIVIDE ET IMPERA” .......................................................................................288
Pașii metodei .............................................................................................................289
Aplicații practice .......................................................................................................290
METODA “BACKTRACKING” ...........................................................................................298
Backtracking nerecursiv ............................................................................................299
Backtracking recursiv ................................................................................................300
Pașii metodei .............................................................................................................302
Aplicații practice .......................................................................................................303
BIBLIOGRAFIE ..............................................................................................................309

vi
Algoritmică şi programare

Lista figurilor
FIG. 1 TRANSMITEREA INFORMAȚIEI .....................................................................................18
FIG. 2 TRANZIȚIA DE LA DATE LA CUNOȘTINȚE .....................................................................19
FIG. 3 SISTEMUL DE CALCUL .................................................................................................21
FIG. 4 REPREZENTARE TEHNICĂ A VALORILOR LOGICE 0 ȘI 1 PENTRU UN BIT........................23
FIG. 5 SIMBOLURI GRAFICE (BLOCURI STANDARD) ................................................................36
FIG. 6 SCHEMA LOGICA PENTRU REZOLVAREA ECUAȚIEI DE GRADUL I .................................36
FIG. 7 REPREZENTAREA ALGORITMULUI PRIN SCHEMĂ LOGICĂ ............................................38
FIG. 8 PRECEDENȚA OPERATORILOR .....................................................................................45
FIG. 9 INSTRUCȚIUNI REPETITIVE CU TEST (INIȚIAL SAU FINAL) ............................................54
FIG. 10 FAZE ȘI ETAPE ÎN EXISTENȚA UNUI PRODUS PROGRAM ..............................................61
FIG. 11 CICLUL DE REALIZARE A UNUI PRODUS PROGRAM (APLICAȚIE SAU SISTEM
INFORMATIC) ................................................................................................................ 62
FIG. 12 FAZELE DE REALIZARE ALE UNUI PROGRAM..............................................................66
FIG. 13 ARHITECTURA CLIENT-SERVER 2-TIER (CU DOUĂ PĂRȚI) .........................................83
FIG. 14 ARHITECTURA CLIENT-SERVER 3-TIER ȘI N-TIER ......................................................84
FIG. 15 INTERFAȚA GRAFICA UTILIZATOR RAPTOR .............................................................88
FIG. 16 CALCULUL ARIEI CERCULUI IN RAPTOR .................................................................90
FIG. 17 INSERAREA SIMBOLURILOR .......................................................................................91
FIG. 18 EDITARE SIMBOL INPUT ............................................................................................92
FIG. 19 EDITARE SIMBOL DE ATRIBUIRE ................................................................................92
FIG. 20 EDITARE SIMBOL OUTPUT .........................................................................................93
FIG. 21 EXECUȚIA PROGRAMULUI/ALGORITMULUI................................................................94
FIG. 22 ALGORITMI PENTRU CALCULUL ARIEI CERCULUI ......................................................94
FIG. 23 SUNA A DOUĂ NUMERE .............................................................................................95
FIG. 24 ARIA ȘI PERIMETRUL DREPTUNGHIULUI ....................................................................96
FIG. 25 ARIA ȘI PERIMETRUL DREPTUNGHIULUI - ALGORITMI OPTIMIZAȚI ............................96
FIG. 26 ALGORITM PENTRU REZOLVAREA ECUAȚIEI DE GRADUL I ........................................97
FIG. 27 SCHEMA LOGICA 1 PENTRU REZOLVAREA ECUAȚIEI AX2+BX+C=0) ..........................98
FIG. 28 SCHEMA LOGICA 2 PENTRU REZOLVAREA ECUAȚIEI AX2+BX+C=0) ..........................99
FIG. 29 ALGORITM PENTRU SUMA GAUSS .............................................................................99
FIG. 30 SUMA PĂTRATELOR/CUBURILOR PRIMELOR N NUMERE NATURALE .........................100
FIG. 31 CALCULUL SUMEI K(K+1) .......................................................................................100
FIG. 32 CALCULUL SUMEI K/(K+1) ......................................................................................101
FIG. 33 CALCULUL SUMEI ((K^2+2K+1)/(K^2+2K+2)) .......................................................101
FIG. 34 CALCULUL N!..........................................................................................................102
FIG. 35 CALCUL COMBINĂRI ...............................................................................................102
FIG. 36 CALCUL ARAMJAMENTE .........................................................................................103
FIG. 37 ALGORITM NUMERE PRIME .....................................................................................103
FIG. 38 CALCULUL FACTORIALULUI UTILIZÂND SUBSCHEME LOGICE..................................104
FIG. 39 CALCULUL FACTORIALULUI UTILIZÂND O PROCEDURĂ ...........................................104
FIG. 40 CITIREA UNUI NUMĂR NATURAL NENUL, DETERMINAREA ȘI AFIȘAREA CIFRELOR SALE
...................................................................................................................................105
FIG. 41 DETERMINAREA ȘI AFIȘAREA INVERSULUI UNUI NUMĂR .........................................106
FIG. 42 TEST PALINDROM ....................................................................................................106

vii
Algoritmică şi programare

FIG. 43 MULȚIMEA PALINDROMURILOR PANA LA N.............................................................107


FIG. 44 MULȚIMEA DE PALINDROMURI DINTR-UN INTERVAL ..............................................107
FIG. 45 SUMA/PRODUSUL CIFRELOR UNUI NUMĂR NATURAL NENUL, CITIT DE LA TASTATURĂ
...................................................................................................................................108
FIG. 46 PRODUSUL CIFRELOR UNUI NUMĂR NATURAL, UTILIZÂND O SUBSCHEMĂ LOGICĂ ..108
FIG. 47 PRODUSUL CIFRELOR UNUI NUMĂR NATURAL, UTILIZÂND O PROCEDURĂ ...............109
FIG. 48 PRODUSUL/SUMA CIFRELOR UNUI NUMĂR NATURAL, UTILIZÂND PROCEDURI .........109
FIG. 49 ARANJAMENTE ȘI COMBINĂRI UTILIZÂND O SUBSCHEMĂ LOGICĂ ...........................110
FIG. 50 CONVERSIA UNUI NUMĂR DIN BAZA 2 ÎN BAZA 10 ..................................................110
FIG. 51 CONVERSIA UNUI NUMĂR DIN BAZA 10 ÎN BAZA 2 ..................................................111
FIG. 52 CITEȘTE N ...............................................................................................................111
FIG. 54 ALGORITM PENTRU CITIREA ȘI SCRIEREA ELEMENTELOR VECTORULUI ...................112
FIG. 53 VECTOR. CITIRE ȘI SCRIERE ELEMENTE...................................................................112
FIG. 55 ALGORITM PENTRU CALCULUL SUMEI/PRODUSULUI ELEMENTELOR UNUI VECTOR .113
FIG. 56 ELEMENTELE COMUNE A DOI VECTORI ....................................................................113
FIG. 57 SORTARE ELEMENTE VECTOR (CRESCĂTOARE) .......................................................113
FIG. 58 SCRIERE SI CITIRE ELEMENTE MATRICE ...................................................................114
FIG. 59 CITIRE /SCRIERE ELEMENTE MATRICE, SUMA ELEMENTELOR DIAGONALEI PRINCIPALE
SI SECUNDARE ............................................................................................................ 116
FIG. 60 SORTARE ELEMENTE MATRICE PE LINIA K ...............................................................117
FIG. 61 NIVELURI DE PRECEDENȚĂ .....................................................................................128
FIG. 62 STRUCTURA ............................................................................................................199
FIG. 63 AFIȘARE UNIUNE .....................................................................................................207
FIG. 64 CONSTANTE SIMBOLICE[1] .....................................................................................226
FIG. 65 FUNCȚII TRIGONOMETRICE[1] .................................................................................227
FIG. 66 FUNCȚII PUTERE ȘI RADICAL[1]...............................................................................227
FIG. 67 FUNCȚII EXPONENȚIALE, LOGARITMICE ȘI HIPERBOLICE[1] ....................................228
FIG. 68 FUNCȚII PARTE ÎNTREAGĂ, DE ROTUNJIRE ȘI DE TRUNCHIERE[1] ............................229
FIG. 69 FUNCȚII MODUL[1] .................................................................................................229
FIG. 70 METODA “DIVIDE ET IMPERA”................................................................................289
FIG. 71 SUMA GAUSS ..........................................................................................................290
FIG. 72 N FACTORIAL...........................................................................................................292
FIG. 73 MAXIMUL DINTR-UN VECTOR DE NUMERE REALE ...................................................293

viii
Algoritmică şi programare

Lista tabelelor
TABEL 1 MULTIPLI DE BIT ȘI OCTET ......................................................................................23
TABEL 2 PUTERILE LUI 2 .......................................................................................................27
TABEL 3 INTERDEPENDENȚA SISTEMELOR DE NUMERAȚIE ....................................................29
TABEL 4 CLASIFICAREA LIMBAJELOR DE PROGRAMARE DUPĂ NIVEL ....................................72
TABEL 5 CARACTERE SPECIALE ..........................................................................................121
TABEL 6 CARACTERE DE SPAȚIERE .....................................................................................121
TABEL 7 TIPURI DE DATE PRIMARE .....................................................................................124
TABEL 8 CONSTANTE DE TIP CARACTER..............................................................................124
TABEL 9 CUVINTE CHEIE .....................................................................................................126
TABEL 10 STRUCTURI .........................................................................................................202
TABEL 11 UNIUNI ...............................................................................................................206
TABEL 12 FUNCȚII DE INTRARE/IEȘIRE PENTRU CARACTERE[1] ..........................................218
TABEL 13 FUNCȚII DE INTRARE/IEȘIRE CU FORMAT[1] ........................................................219
TABEL 14 FUNCȚII CU FORMATARE PENTRU ȘIRURI DE CARACTERE[1] ...............................220
TABEL 15 FUNCȚII PENTRU GESTIUNEA TIMPULUI[1] ..........................................................222
TABEL 16 SPECIFICATORII DE FORMAT PENTRU AFIȘAREA TIMPULUI ..................................223
TABEL 17 FUNCȚII DE CONVERSIE[1] ..................................................................................228
TABEL 18 FUNCȚII PENTRU GENERAREA NUMERELOR ALEATOARE[1] ................................229
TABEL 19 FUNCȚII PENTRU CONCATENARE[1] ....................................................................234
TABEL 20 FUNCȚII PENTRU COPIEREA ȘIRURILOR DE CARACTERE[1] ..................................235
TABEL 21 FUNCȚII PENTRU COMPARAREA A DOUĂ ȘIRURI DE CARACTERE[1] .....................235
TABEL 22 FUNCȚII DE CĂUTARE[1] .....................................................................................236
TABEL 23 FUNCȚII DE SETARE[1] ........................................................................................236
TABEL 24 FUNCȚII DE APARTENENȚĂ[1] .............................................................................246
TABEL 25 FUNCȚII PENTRU CONVERSIA CARACTERELOR[1]................................................246
TABEL 26 FUNCȚII STANDARD PENTRU GESTIUNEA DINAMICĂ A MEMORIEI[1] ...................257
TABEL 27 FUNCȚII PENTRU OPERAȚII CU BLOCURI DE MEMORIE[1].....................................264
TABEL 28. DESCHIDEREA ȘI/SAU CREAREA UNUI FIȘIER TEXT[1] ........................................275
TABEL 29 DESCHIDEREA ȘI/SAU CREAREA UNUI FIȘIER BINAR [1].......................................276
TABEL 30 DESCHIDEREA ȘI/SAU CREAREA UNUI FIȘIER .......................................................276

ix
Algoritmică şi programare

x
Algoritmică şi programare

Bazele informaticii
Noțiuni introductive
Informatica (cf. DEX) reprezintă știința care se ocupă cu studiul
prelucrării informației cu ajutorul mijloacelor automatice de calcul.
Provenind din informație și automatică, pentru prima dată, termenul
informatik a fost introdus în 1957 de Karl Steinbuch în eseul “Informatica:
prelucrarea automată a informației” și a fost adoptat oficial de Academia
Franceză în 1967 (Informatique) și răspândit în alte limbi europene. În
engleză: Computer Science.
În sens restrâns, informatica este o ramură a matematicii, care studiază
aspecte abstracte, formale ale prelucrării informației. În sens larg,
informatica este ansamblul științelor și tehnicilor care se ocupă de
procesarea (prelucrarea) informației. Remarcăm, deci, că informatica nu se
ocupă de studierea calculatorului sau altor echipamente de calcul, ci a
modalităților de tratare a informației cu ajutorul calculatorului.
Știința informației (engleză: information science; franceză: science de
l'information) este o știință interdisciplinară, care se ocupă cu studierea
problemelor privind culegerea, clasificarea, depozitarea, regăsirea și
diseminarea informației. A apărut cu mult timp înaintea informaticii,
deoarece se referă la utilizarea bibliotecilor, arhivelor și a altor mijloace de
manipulare și conservare a informației, atât tradiționale, cât și moderne.
Domeniul informaticii este în ultimul timp legat de domeniul
comunicațiilor, atât datorită tehnicilor digitale folosite în ambele domenii
cât și datorită tendinței de integrare a producerii, transportului și consumului
de informație. Astfel, a apărut „Tehnologia Informației și Comunicațiilor”
TIC (în engleză „Information and Communication Technologies” ICT), care
unifică cele trei categorii de informații de bază (date, sunete și imagini) și le
tratează unitar după digitizare (adică după transformarea din semnal în
succesiuni de numere binare), iar prelucrarea se face cu același tip de
echipamente IT(calculatoare numerice), folosind programe adecvate.
Tehnologia informației (TI) numită și tehnologia informației și
comunicațiilor (TIC) este domeniul științific și tehnic care se ocupă cu
metodele și procedeele de achiziție, prelucrare, stocare, transport și
prezentare a informațiilor, prin echipamente și rețele („hardware”) și
aplicații („software”) create în acest scop cât și administrarea sistemelor
respective. Cuprinde tehnologiile aplicate la utilizarea calculatoarelor
electronice și rețelelor de calculatoare pentru culegerea, conversia, stocarea,
procesarea, transmiterea, protejarea și diseminarea informației.

15
Algoritmică şi programare

Conceptele utilizate în metodele și procedeele amintite în definiția de


mai sus provin, în general, din alte domenii de activitate umană, de exemplu
din editare și tipărire de carte (tipuri de caractere, spațieri, etc.), din artă și
design (grafică, proporții, combinații de culori, etc.), tehnică și economie (în
aplicații ce vizează aceste domenii). Prin implicarea sa în toate activitățile
umane domeniul TIC a devenit foarte vast, antrenând specialiști din cele
mai diverse profesiuni; de aceea, uneori, delimitarea metodelor specifice
TIC și a cunoștințelor necesare celor ce se ocupă în acest domeniu nu
prezintă frontiere clare și nici stabile. În esența sa TIC este un domeniu
multi și inter-disciplinar, axat pe conceptualizarea informațiilor de orice
natură, cu reprezentarea și prelucrarea unitară a acestora.
Cu toată această diversitate, utilizarea TIC presupune un set relativ
restrâns de metode de uz general și de „obișnuințe” de operare (lucru cu
calculatorul) pentru un registru larg de utilizatori, deoarece chiar extinderea
TIC a impus două caracteristici - ca cerințe de bază:
• atributul deschis (în engleză „open”) – prin care modificări în
structura și complexitatea unui sistem de calcul (ca echipamente și
programe) nu trebuie să afecteze modul de lucru al utilizatorului;
• atributul prietenos (în engleză „friendly”) – prin care operarea nu
trebuie să necesite cunoștințe speciale (de exemplu nume de
comenzi, sintaxă, etc.), manevrele trebuie să fie simple, iar
sistemul să ofere sugestii și ajutor în lucrul utilizatorului.
În vederea lucrului comod și uniform al utilizatorului în rețele de
calculatoare eterogene (adică cele care interconectează echipamente și
programe de la diferiți producători), s-a impus o a treia caracteristică
deziderat, anume:
• transparența pentru utilizator a tipului de echipament sau program
care oferă un anumit serviciu.
Urmare a acestui atribut, utilizatorul nu trebuie să fie interesat de tipul
și locul echipamentului sau programului pentru consumarea unui serviciu ci
să opereze la fel de oriunde în rețea. Astfel, au apărut standarde
internaționale care stabilesc modul cum trebuie să funcționeze și chiar cum
să se prezinte către utilizator o aplicație de uz general (de exemplu,
aplicațiile CLOUD).
Informații și prelucrări
Date
Fără a intra în studiul cunoașterii, se poate conchide din considerațiile
anterioare că, pentru manipularea pieselor de cunoaștere prin intermediul
Tehnologiei Informației și a Comunicațiilor (TIC), acestea trebuie să se
refere la o formă: materială (substanță sau câmp), ori conceptuală (cuvânt).

16
Algoritmică şi programare

Constituie informație orice text, număr, imagine, sunet, dar și miros,


rugozitate, căldură, sesizabile și utile omului la un moment dat.
În cazul său cel mai simplu, informația se referă la apariția (sau ne-
apariția) unui eveniment, informația căpătată fiind de tip binar:
Adevărat/Fals, Da/Nu. Efectiv, informația apare după consumarea
evenimentului, adică atunci când piesa de cunoaștere este sigură și clară;
spunem că informația s-a instanţiat – în sensul că a căpătat o valoare pentru
o mărime legată de eveniment.
Omul manipulează însă și informații care nu sunt sigure (sunt
probabile sau sunt posibile) precum și informații care nu sunt clare (sunt
vagi). Totuși, acestea pot fi prelucrate și utilizate folosind tehnica de calcul,
prin metode stochastice (probabilități) sau metode posibiliste, respectiv prin
metode de inteligență artificială cum sunt tehnicile „Fuzzy”.
În final, forma – ca purtător al informației, trebuie să capete o
reprezentare ce permite memorarea ei în dispozitivele electronice
(magnetice, optice, SSD, etc.) ale unui sistem de calcul, devenind astfel
„dată”.
Datele sunt informații într-o reprezentare adecvată stocării lor pe un
suport (magnetic, optic, etc.), în scopul prelucrării electronice sau
transferului electromagnetic. Cu alte cuvinte, ”Datele” reprezintă “materia
brută” din care este constituită informația. Datele nu au semnificație prin ele
însele, ci numai într-un anumit context.
Reprezentarea datelor în calculatoarele numerice se face prin
discretizare. Acest procedeu indică o separare în piese (discrete) a
elementelor ce compun informația. De exemplu, o imagine este discretizată
prin puncte minuscule (pixeli) - fiecare cu o culoare și o intensitate
luminoasă, care prin ansamblul lor refac imaginea vizată. Acestor puncte le
corespund coduri ce reprezintă culoarea și intensitatea luminoasă și sunt
aranjate pe linii și coloane, ca într-o matrice, fiind memorată apoi pe suport
extern, în memoria calculatorului sau prezentată pe ecran. Toate informațiile
în calculatoarele numerice sunt reprezentate prin coduri (numerice), pentru a
fi ulterior stocate și prelucrate după cerințe. Ca semnificație, discretizarea se
referă la separarea unor momente de timp egale în evoluția unui fenomen
real (și care prezintă de fapt o curgere continuă), în scopul cunoașterii sau
modelării acestuia; prin extensie, se consideră discretizare și descompunerea
în piese spațiale a unui obiect (de exemplu o imagine). Descompunerea în
piese de mărimi egale ale unei măsuri (distanță, tensiune electrică, etc.) se
numește cuantificare; acesta este procedeul prin care măsurările efectuate în
lumea reală sunt separate în piese discrete, spre a fi stocate și prelucrate cu
tehnica de calcul numerică.

17
Algoritmică şi programare

Informație
Informația este o piesă de cunoaștere legată de o utilitate umană,
așteptată și/sau obținută în urma unui eveniment, apoi stocată sau transmisă
de la sursă/emițător către receptor printr-un canal de comunicație (materie
sau câmp).
Pentru domeniul TIC, informația este și „materia primă” și „produsul
final”. Informația nu poate fi separată de o utilitate, o finalitate umană și
este în legătură cu procesul de cunoaștere. Varietatea formelor, situațiilor și
utilităților în care este implicată cunoașterea umană, face ca informația să nu
poată fi definită fără echivoc.
În vorbirea curentă, informația este o comunicare, o știre care pune
pe cineva la curent cu o anumită situație sau cu un anumit eveniment. Este
important caracterul de noutate: pentru a fi informație, mesajul trebuie să
conțină elemente pe care primitorul/receptorul nu le cunoștea anterior.
În Știința informației, informația este conținută în documente, în
format tradițional sau electronic. În biologie, informația constă în semnalele
(vizuale, auditive, tactile etc.) pe care organismul le primește din mediul
exterior. Există, de asemenea, informația genetică (înregistrată în gene). În
tehnică, informația constă din semnalele primite de la senzori sau transmise
prin căi de comunicație. Orice cunoștință umană este legată de informații,
iar acestea din urmă pot apare ca imagini, sunete (surse directe) sau numere,
texte (surse deja prelucrate).

Fig. 1 Transmiterea informației


În general, o cunoștință umană surprinde o legătură cauză-efect din lumea
înconjurătoare, iar procesul de obținere a cunoștințelor este relativ complex,
adică:
a) se preiau informații din mediu și se asociază cu evenimentele de
interes (de obicei o mulțime de evenimente legate de o utilitate
anume);

18
Algoritmică şi programare

b) se constată repetarea apariției unui anume eveniment așteptat (numit


efect) în corelație cu unul sau mai multe evenimente observate
(numite cauze) și se stabilește o relație de cauzalitate;
c) se procedează la generalizarea relației de cauzalitate formulând o
„lege” – eliminând, eventual, evenimente ce nu intră în mod relevant
în relația de cauzalitate (având caracter întâmplător sau legături slabe
cu evenimentul efect).
De ce se vorbește însă, de o tehnologie a informației? După cum prelucrările
pe care le suferă materiile prime într-o uzină spre a se obține un anume
produs se bazează pe o tehnologie – ca succesiune bine stabilită de
transformări efectuate cu un anumite instalații special proiectate,
asemănător, informația din mediu este prelucrată într-o succesiune bine
definită de operații cu echipamente IT dedicate. Pe această similitudine se
poate concepe o paralelă între o tehnologie industrială și tehnologia
informațiilor, care poate fi sintetizată astfel: materie primă – pregătire –
transformare - ambalare a produsului finit, respectiv informație –
reprezentare – prelucrare – prezentare a rezultatelor către utilizator.
Cunoștințe
Pe scurt, la diferite niveluri de reprezentare, în informatică se operează cu
date, informație și cunoștințe.
Datele nu au semnificație prin ele însele, reprezintă “materia brută” din care
este constituită informația. Cu alte cuvinte, datele doar când capătă o
semnificație devin informație. Informația astfel înțeleasă se transformă în
cunoștințe.

Fig. 2 Tranziția de la date la cunoștințe


Cunoștințele sunt informații înțelese, acumulate și sistematizate, care pot fi
utilizate pentru a obține anumite rezultate (pentru a lua decizii, pentru a
acționa etc.). Cunoștințele acumulate de un om constituie un ansamblu
sistematizat de concepte și de reguli de acțiune. Înțelegerea unei informații
constă tocmai în încadrarea ei în sistemul de cunoștințe deja acumulate.
Sistemul informațional
Se numește sistem un ansamblu de elemente care interacționează atât între
ele și care poate fi privit ca un întreg. Interacțiunile dintre elementele
sistemului, sau dintre sistem și mediu, se pot realiza prin schimb de
substanță, de energie sau de informație. În informatică interesează, desigur,
sistemele în care are loc transfer și prelucrare de informație.

19
Algoritmică şi programare

Sistemul informațional (engleză: information system, franceză: système


d'information) este un ansamblu organizat de elemente, implicate în
procesul de colectare, transmisie, stocare, prelucrare și diseminare a
informației. Interacțiunea dintre elementele sistemului informațional se
realizează prin transmiterea de informație și nu este obligatoriu ca operațiile
enumerate anterior să se efectueze cu echipamente de calcul. De altfel,
sistemele informaționale au existat cu mult înaintea apariției calculatoarelor.
Sistemul informațional este compus din:
• totalitatea informațiilor cu care operează;
• echipamentele folosite pentru operații asupra informației;
• personalul implicat în funcționarea și administrarea sistemului;
• procedurile aplicate pentru funcționarea sistemului.
Sistemul de calcul
Tehnologia Informației și Comunicațiilor (TIC) este privită ca
domeniu de „înaltă tehnologie” (High Technology), atât prin subtilitatea și
complexitatea metodelor și aparaturii electronice implicate cât și prin
aplicațiile cu impact și eficiență deosebite pentru toate domeniile de
activitate. Utilizarea Tehnologiei Informației și Comunicațiilor are cea mai
largă extindere întâlnită la orice altă tehnologie cunoscută, datorită materiei
prime pe care o prelucrează - informația. Cum nu există domeniu și
activitate umană care să nu folosească informații, TIC vine să sprijine
activitățile din acel domeniu, prin metode și mijloace pentru prelucrarea
informațiilor, unele generale, altele specializate.
Se numește sistem de calcul complexul de echipamente fizice
interconectate, cu programele și datele legate intrinsec de funcționarea și
utilizarea lor de către om.
Un sistem de calcul conține „partea tare” – hardware, echipamentele,
precum și „partea moale” – software, programele și datele.
Echipamentele (engleză: hardware) sunt realizate practic sub formă
de aparate sau dispozitive electronice, electrice, mecanice sau optice.
Programele (engl.: software) sunt seturi de instrucțiuni , scrise într-un
limbaj de programare , pe care calculatorul le executa pentru a îndeplini o
anumita sarcina (prelucrarea informației).
Sistemul de operare constituie interfața dintre partea hardware si
utilizator si gestionează resursele fizice si logice ale sistemului de calcul.

20
Algoritmică şi programare

Fig. 3 Sistemul de calcul


Cuvântul calculator are, în limba română, mai multe semnificații. În
ceea ce ne privește, vom avea în vedere calculatorul electronic numeric
universal, numit și computer ori PC (cuvânt preluat din limba engleză) sau
ordinator (în franceză: ordinateur). Acesta efectuează cu ajutorul software-
ului diferite prelucrări de date numerice și nenumerice.
Se numește calculator numeric (engl.: digital computer), deoarece în
interiorul lui toată informația este reprezentată prin numere. Se numește
universal deoarece funcționează pe bază de program înregistrat în memorie.
Prin schimbarea programului se poate modifica funcționarea acestuia (deci
succesiunea de operații pe care le execută), fără a face modificări în
construcția calculatorului.
Programul este și el codificat numeric.

21
Algoritmică şi programare

Reprezentarea și structurarea informației


Informațiile vehiculate de om, în diferite situații, sunt legate de
mijloacele prin care acesta comunică cu mediul înconjurător și cu ceilalți
oameni. În primul caz, informațiile apar sub formă de imagini și sunete, iar
în al doilea caz informațiile apar în plus sub formă de texte (cărți, reclame,
etichete de produs) și sub formă de numere (sume de bani, vârste). În scopul
manipulării acestor tipuri de informație prin intermediul tehnicii de calcul,
fiecare din categoriile de mai sus capătă o reprezentare anume, pentru a fi
„inteligibilă” calculatorului numeric.
Reprezentarea informației în creierul uman nu este încă elucidată -
datorită complexității structurilor din creier și datorită mecanismelor, încă
necunoscute, ce realizează stocarea informațiilor. În calculatoarele
electronice, dispozitivele de stocare au doar două stări și sunt astfel simplu
de realizat tehnologic: condensatoare cu stările încărcat/descărcat sau
dispozitive cu două stări stabile (numite bi-stabile). In alte variante
tehnologice ele sunt de asemenea, ușor de realizat: mecanic, chimic și chiar
la nivel atomic.
Calculatoarele actuale sunt denumite calculatoare numerice (sau
digitale – de la cuvântul englezesc „digit” - cifră) pentru că ele transformă
toate informațiile pe care le stochează și prelucrează în reprezentări discrete.
Au existat și calculatoare analogice, în care informația păstra reprezentarea
continuă, firească, a semnalelor din lumea înconjurătoare; aceste
calculatoare prelucrau semnalele continui cu ajutorul „dispozitivelor
electronice analogice” de tip amplificator de semnal, integrator cu
condensator electric, etc. Limitările calculatoarelor analogice privind
aplicațiile și modalitatea de stocare a datelor au dus la înlocuirea lor cu
dispozitive numerice, care prin discretizare și cuantificare pot reprezenta și
prelucra orice semnal analogic (cu o precizie mai mare sau mai mică).
Bit, octet și multiplii acestora
Anterior, definiția informației a fost însoțită de exemplificări relative
la tipuri de informații – cum sunt de exemplu imagini, sunete, numere, text.
Informațiile logice, de tip Adevărat/Fals sau Da/Nu, sunt informații
abstracte, putând fi considerate piese elementare de informație fiindcă au
doar două valori posibile și reprezintă situații simple sau generale – de
exemplu un eveniment oarecare se produce (Da) sau nu se produce (Nu).
Piesa de informație ce poate avea doar două valori posibile se numește
bit.
Bit-ul reprezintă unitatea elementară de informație, corespunzând unei
situații fizice în care un comutator este deschis sau închis, situații

22
Algoritmică şi programare

reprezentate de oameni, în afara calculatorului prin cifrele 0 și 1, deci,


cifrele sistemului binar.

0 1

Fig. 4 Reprezentare tehnică a valorilor logice 0 și 1 pentru un bit

În calculatoarele numerice, un bit se consideră că poate lua ori


valoarea 0 ori valoarea 1, fiecare din acestea corespunzând de fapt unei
situații fizice în care un comutator este deschis – respectiv închis (vezi
fig.4), sau un condensator electric este încărcat cu sarcină electrică –
respectiv este descărcat. Valorile 0 sau 1 reprezintă deci „valori logice”
(Adevărat/Fals) dar totodată, fiind cifre, permit crearea de numere.
Memoriile semiconductoare ale calculatoarelor numerice pot stoca biți
în miliarde de condensatoare minuscule, numărul acestor biți reprezentând
capacitatea de memorie a respectivului calculator. Asemenea numere uriașe
nu sunt comod de utilizat, de aceea se utilizează multipli de bit.
Octet (sau Byte) este un grup de opt biți reprezentând, tradițional, o
celulă de memorie. Evident ca evaluarea capacităților de memorie actuale,
folosind ca unitate de măsură octetul, nu rezolvă problema numerelor foarte
mari vehiculate. De aceea se folosesc multipli de octet, cu denumiri uzuale
ale multiplilor unităților de măsură, dar cu semnificație diferită - conform
tabelului de mai jos.

Nume multiplu Semnificație Magnitudine


× 1.024
10
Kilo 2
× 1.048.576
20
Mega 2
× 1.073.741.824
30
Giga 2
× 1.099.511.627.776
40
Tera 2

Tabel 1 Multipli de bit și octet


După cum se observă din Tabel 1 semnificația multiplilor de bit și
octet în informatică diferă de cea tradițională (în care Kilo reprezintă 103 -
de exemplu), dar valoarea numerică a fiecărei categorii de multiplu este
oarecum apropiată de cea tradițională (Kilo apropiat de mie, Mega de

23
Algoritmică şi programare

milion, etc.). Rațiunea acestor valori pentru multiplii utilizați în informatică


este reprezentarea în calculator a numerelor folosind baza 2 de numerație.
Tipuri de date simple
Varietatea informațiilor utilizate de om pare foarte mare, însă ele pot
fi clasificate în categorii generice care apoi să primească reprezentări
generale ca tipuri de date. Se poate face o analiză sumară din care rezultă
câteva categorii de asemenea informații simple:
• numere singulare – utilizate a desemna sume de bani, distanțe, zile
din luna calendaristică, cu care se pot face calcule matematice. Se
deosebesc două categorii de numere – numere întregi și numere
reale (cu zecimale).
• simboluri grafice - litere, cifre, semne de punctuație, spatii libere,
utilizate în texte. Aceste simboluri sunt elemente ale unui set finit
de simboluri de scriere (alfabetul, cifrele de la 0 la 9, semnele de
punctuație); ele sunt denumite generic caractere alfanumerice.
• Informații de tip Adevărat sau Fals – pentru a desemna o situație
sau un eveniment funcție de care se ia o decizie (de exemplu, dacă
un eveniment a avut loc se execută o anumită acțiune, altfel altă
acțiune), denumite informații logice (booleene).

Reprezentarea interna a datelor se face binar utilizându-se o


codificare. Aceasta codificare poate fi:
• ASCII – cea mai utilizată tabelă de cod, provenind din
standardul american (American Standard Code for Information
Interchange);
• Unicode – pe 16 biți, care este o altă extensie a tabelei ASCII
și care are un număr mare de coduri disponibile, pentru
reprezentarea nu numai a caracterelor latine cu diverse
diacritice dar și a caracterelor diferite de cel latin (chirilic,
arab, etc.)
Sisteme de numerație
Sistemul de numerație reprezintă o modalitate de notare matematică
și se definește ca fiind totalitatea regulilor folosite în vederea scrierii
numerelor cu ajutorul simbolurilor de scriere. Un sistem de numerație este
nepozițional atunci când simbolurile de scriere prin care sunt reprezentate
cantitățile (numerele/ dimensiunea) nu au o pondere în funcție de poziția
ocupată în cadrul șirului de simboluri ce desemnează (semnifică) cantitatea
globală (totală). Exemplu cel mai elocvent îl reprezintă sistemul de
numerație roman. Un sistem de numerație este pozițional atunci când

24
Algoritmică şi programare

aportul unui simbol de scriere (cifră sau literă) în valoarea totală a unui
număr depinde atât de valoarea simbolului de scriere, cât și de locul ocupat
de acesta în reprezentarea numărului respectiv.
Baza unui sistem de numerație pozițional reprezintă numărul de
semne distincte necesare scrierii unui număr sau totalitatea unităților de
același ordin de mărime care formează o unitate de ordin imediat superior.
𝑛𝑢𝑚ă𝑟(𝑏𝑎𝑧ă) = [𝑑𝑛 … 𝑑2 𝑑1 𝑑0 ](𝑏) = ∑𝑛𝑖=0 𝑑𝑖 𝑏 𝑖 =
𝑑0 𝑏 0 + 𝑑1 𝑏1 + ⋯ + 𝑑𝑛 𝑏 𝑛 ;
b - baza sistemului de numerație;
𝑑𝑖 – numărul/simbolul de scriere aflat pe poziția i ;
n- număr (pozitiv sau negativ) ce indica poziția sau rangul;
n+1=numărul de simboluri de scriere;

Sisteme de numerație:
• sisteme în baza 1 (unary numeral system sau bijective base
system) – Sistemul de vot/pontaj
• sisteme în baza 2 (binary numeral system) – Digital
computing
• sisteme în baza 5 (quinary numeral system) - Sistemul
Roman
• sisteme în baza 8 (octal numeral system) - Informatică
• sisteme în baza 10(decimal numeral system) - Sistemul Arab
• sisteme în baza 16(hexazecimal numeral system) -
Informatică
• sisteme în baza 20 (vigesimal numeral system) - Sistemul
Mayaș
• sisteme în baza 60 (sexazecimal numeral system) – Sistemul
Babilonian.
Sistemul de numerație zecimal (baza 10)
Numerele întrebuințate frecvent pentru calcule (și nu numai), cu cea
mai mare arie de răspândire și aplicabilitate, sunt formulate în așa-numita
„scriere arabă”, în care numărul 10 este considerat „bază de numerație” iar
cifrele sunt plasate pe ranguri corespunzătoare puterilor cifrei 10.
În reprezentarea zecimală a numerelor, rangul conține cifre de la 0 la
9, astfel, pornind de la stânga către dreapta, unitățile au rangul 0, zecile au
rangul 1, sutele au rangul 2, etc. Numărul simbolurilor utilizate pentru
reprezentarea numerică este 10 (exact cât baza de numerație), respectiv
cifrele de la 0 la 9. În concluzie, un număr este reprezentat în sistemul de

25
Algoritmică şi programare

numerație zecimal ca fiind suma dintre produsul fiecărui simbol de scriere și


numărul 10 ridicat la puterea rangului respectivului simbol de scriere.

𝑛𝑢𝑚ă𝑟(𝑏𝑎𝑧ă 10) = [𝑑𝑛 … 𝑑2 𝑑1 𝑑0 ](10) = ∑𝑛𝑖=0 𝑑𝑖 ∗ 10𝑖 =


𝑑0 ∗ 100 + 𝑑1 ∗ 101 + ⋯ + 𝑑𝑛 ∗ 10𝑛 ;
unde, 𝑑𝑖 este numărul/simbolul de scriere aflat pe poziția i.

... Mii Sute Zeci Unități

... 103 102 101 100

... 5 9 9 9

5999 (10) = 9⋅100 + 9⋅101 + 9⋅102 + 5⋅103 =9 + 90 + 900 + 5000


n 8 7 6 5 4 3 2 1 0
...
10 10 10 10 10 10 10 10 10 10
5 4 3

543 (10) = 5×102+4×101+3×100 = 500+40+3


Sistemul de numerație binar (baza 2)
Calculatoarele numerice sunt dispozitive realizate tehnologic în cel
mai simplu mod, prin faptul că folosesc cea mai simplă reprezentare a
informației: Adevărat / Fals sau Da / Nu.
Sistemul de numerație binar utilizează doar 2 cifre în modalitatea de
reprezentare a numerelor, respectiv 0 și 1 astfel, asemănător modalității de
lucru în baza de numerație 10, numărul reprezentat în baza 2 (de numerație)
va avea rangurile drept puteri ale bazei de numerație.
𝑛𝑢𝑚ă𝑟(𝑏𝑎𝑧ă 2) = [𝑑𝑛 … 𝑑2 𝑑1 𝑑0 ](2)
𝑛

= � 𝑑𝑖 ∗ 2𝑖 = 𝑑0 ∗ 20 + 𝑑1 ∗ 21 + ⋯ + 𝑑𝑛 ∗ 2𝑛
𝑖=0
unde, 𝑑𝑖 este numărul/simbolul de scriere aflat pe poziția i.

... rang 3 rang 2 rang 1 rang 0

... 23 22 21 20

... 1 0 1 1

1011 (2) = 1⋅20+1⋅21+0⋅22+1⋅23 = 11 (10)

26
Algoritmică şi programare

n 128 64 32 16 8 4 2 1
n 7 6 5 4 3 2 1 0
2 2 2 2 2 2 2 2 2
1 0 0 0 1 1
100011 (2) = 1×25+0×24+0×23+0×22+1×21+1×20 =32+2+1= 35 (10)

n 0 1 2 3 4 5 6 7 8 9 10 11 12
2𝑛 1 2 4 8 16 32 64 128 256 512 1024 2048 4096
Tabel 2 Puterile lui 2

Sistemul de numerație octal (baza 8)


Sistemul de numerație octal utilizează cifre cu valori de la 0 la 7 în
reprezentarea codificată a numerelor reale dar și de reprezentare a numerelor
raționale și iraționale. Este utilizat in Informatică deoarece reușește să
comprime șiruri lungi de biți din reprezentarea unui număr în baza 2.
Totodată, conversia din baza 2 în baza 8 și invers se face foarte ușor, fără
calcule laborioase:

𝑛𝑢𝑚ă𝑟(𝑏𝑎𝑧ă 8) = [𝑑𝑛 … 𝑑2 𝑑1 𝑑0 ](8)


𝑛

= � 𝑑𝑖 ∗ 8𝑖 = 𝑑0 ∗ 80 + 𝑑1 ∗ 81 + ⋯ + 𝑑𝑛 ∗ 8𝑛
𝑖=0

7 6 5 4 3 2 1 0
8 8 8 8 8 8 8 8
4 3 0 7

4307 (8) = 4×83+3×82+0×81+7×80= 2247 (10)


Alte exemple:
27 (8) = 2×81+7×80 = 16+7 = 23 (10)
31 (8) = 3×81+1×80 = 24+1=25 (10)
55 (8) = 5×81+5×80 = 40+5=45 (10)
Sistemul de numerație hexazecimal (baza 16)
Sistemul de numerație hexazecimal este utilizat cu preponderență în
matematică și informatică deoarece, similar sistemului de numerație octal,
reușește să comprime șiruri lungi de biți, prin gruparea lor câte 4, asigurând
astfel o reprezentare mult mai apropiata de înțelegerea umană a unui număr
reprezentat binar. Sistemul hexazecimal, pentru a reprezenta cele 16 cifre
utilizează 16 simboluri de scriere distincte: primele 10 dintre acestea coincid
cu cifrele sistemului de numerație zecimal, iar pentru restul de 6 utilizează
primele litere ale alfabetului latin (A,B,C,D,E,F).

27
Algoritmică şi programare

𝑛𝑢𝑚ă𝑟(𝑏𝑎𝑧ă 16) = [𝑑𝑛 … 𝑑2 𝑑1 𝑑0 ](16)


𝑛

= � 𝑑𝑖 ∗ 16𝑖 = 𝑑0 ∗ 160 + 𝑑1 ∗ 161 + ⋯ + 𝑑𝑛 ∗ 16𝑛


𝑖=0

7 6 5 4 3 2 1 0
16 16 16 16 16 16 16 16
B C 1 2

BC12 (16) = 11×163+12×162+1×161+2×160= 48146 (10)


Alte exemple:
28 (16) = 2×161+8×160 = 40 (10)
2F (16) = 2FH = 2×161+15×160 = 47 (10)
Interdependența sistemelor de numerație
Sistemul de numerație binar este utilizat pentru stocarea si prelucrarea
informației în Informatică. Pentru om, dificultatea redării în sistem binar, fie
scris sau oral, crește direct proporțional odată cu numărul de biți. De aceea,
uzual, se folosesc în mod curent sisteme de numerație cu baze mai mari
(indicat ca baza de numerație sa fie egala cu rezultatul lui 2 la o putere
naturală) care permit trecerea rapidă în binar. Din acest punct de vedere,
cele mai întâlnite sisteme de numerație sunt sistemul octal (baza 8) și
sistemul hexazecimal (baza 16). Astfel, orice simbol de scriere din baza 8 se
poate reprezenta printr-o combinație de 3 cifre binare, respectiv orice simbol
de scriere din baza 16 (hexazecimal), printr-o combinație de 4 cifre binare
deoarece 8 = 23 și 16 = 24.

n 0 1 2 3 4 5 6 7 8 9 10 11 12
2𝑛 1 2 4 8 16 32 64 128 256 512 1024 2048 4096

Baza 2 3 4 5 6 7 8 9 10 16
0 0 0 0 0 0 0 0 0 0
1 1 1 1 1 1 1 1 1 1
2 2 2 2 2 2 2 2 2
3 3 3 3 3 3 3 3
4 4 4 4 4 4 4
5 5 5 5 5 5
+ 0 1 6 6 6 6 6
0 0 1 7 7 7 7
1 1 10 8 8 8
9 9
A (10)
B (11)
C (12)
* 0 1 D (13)

28
Algoritmică şi programare

Baza 2 3 4 5 6 7 8 9 10 16
0 0 0 E (14)
1 0 1 F (15)

X( 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
10)
X( 0 1 2 3 4 5 6 7 8 9 A B C D E F 10
16)
X( 0 1 2 3 4 5 6 7 10 11 12 13 14 15 16 17 20
8)
X( 0 1 1 1 1 1 1 1 10 10 10 10 11 11 11 11 10
2) 0 1 0 0 1 1 00 01 10 11 00 01 10 11 00
0 1 0 1 0
Tabel 3 Interdependența sistemelor de numerație

Conversia binar – octal - hexazecimal și invers


După cum am amintit anterior, orice simbol de scriere din baza 8 se
poate reprezenta printr-o combinație de 3 cifre binare, respectiv orice simbol
de scriere din baza 16 (hexazecimal), printr-o combinație de 4 cifre binare
deoarece 8 = 23 și 16 = 24.
Exemple:
4307 8 = 100011000111 2 = 2247 10

2F 16 = 2×161+15×160 = 47 10
2F 16 = 57 8 =5×81+7×80 = 40+70= 47 10
101111 2 = 1×25+1×23+1×22+1×21+1×20 = 47 10

29
Algoritmică şi programare

BC12 16 = 11×163+12×162+1×161+2×160= 48146 10


BC12 16 = 1011110000010010 2
BC12 16 = 136022 8

Conversia binar – zecimal și invers


Trecerea unui număr din sistemul zecimal în binar se poate face prin
diferite metode. Cele mai uzuale sunt: prin împărțirea succesiva a numărului
zecimal la 2 sau prin diferențe succesive dintre numărul zecimal (respectiv
restul) și puterile lui doi.
35 10 =? 2

35 10 =? 2

30
Algoritmică şi programare

Verificare:
35 (10) =32+2+1=25+21+20
100011 (2) = 1×25+1×21+1×20 =32+2+1= 35 (10)

73 10 =? 2

73 10 =? 2

Verificare:
1×26+1×23+1×20 = 64+8+1 = 73 10
Reprezentarea numerelor întregi
Numerele întregi pot fi:
• Pozitive (fără semn)
• Pozitive (cu semn)
• Negative (cu semn)

Numerele întregi pot fi codificate prin trei metode:


• cod direct (semn și valoare absolută)
Numerele întregi se reprezintă prin mărime (valoare
absolută) și semn. Pentru numerele negative, bitul cel mai

31
Algoritmică şi programare

semnificativ (de semn) este 1, iar ceilalți n-1 biți servesc


pentru reprezentarea valorii absolute a numărului.
Ex.: N = -5 pe 8 biți => 10000101
• cod invers (complement față de 1)
Pentru numerele negative, bitul de semn este 1, ceilalți n-1
biți servind pentru reprezentarea valorii absolute negate a
numărului. Negarea se realizează la nivel de bit: biții 0
devin 1 și biții 1 devin 0.
Ex.: N = -5 pe 8 biți => 11111010
• cod complementar (complement față de 2)
Se obține din complementul față de 1, la care se adaugă 1
Ex.: N = -5 pe 8 biți => 11111010+1=11111011
a=5 => N=28-a => 256-5=25110 = 11111011 2

Conversia unui număr dintr-o anumită bază de numerație (α) în altă


bază de numerație (β).
Exemplu: conversia numărului 121,45 din zecimal în binar se face în
doua etape: se convertește partea întreagă in binary apoi se convertește
partea fracționară. Diferența dintre conversia părții întregi și a celei
fracționare este următoarea: partea întreagă se împarte succesiv la 2 pe când
partea fracționară se înmulțește succesiv cu 2 și se reține doar partea
întreagă.
Astfel, din conversia părții întregi rezultă că 121 10 = 1111001 2 .dar
pentru partea fracționară, respective 0,45 conversia se face în felul următor:
0,45 * 2 = 0,9; se reține 0
0,9 * 2 = 1,8; se reține 1
0,8 * 2 = 1,6; se reține 1
0,6 * 2 = 1,2; se reține 1
Algoritmul se oprește în momentul în care partea fracționară devine 0
sau s-a atins precizia dorită (numărul de cifre pentru reprezentarea
rezultatului).
0,45 10 = 0,01110011 2
În final, obținem conversia următoare:
121,45 10 = 1111001,01110011 2

32
Algoritmică şi programare

Probleme propuse:
1. Reprezentați numărul 121 în baza 2 pe 8 biți;
2. Reprezentați numărul 0.121 în baza 2 pe 8 biți;
3. Efectuați în baza 2 suma și produsul numerelor a=1011 și
b=1101;
4. Reprezentați numărul 121.122 în baza 2 pe 16 biți (câte 8
pentru partea întreagă și 8 pentru partea fracționară);
5. Reprezentați în cod complementar numărul 112 pe 8 biți;
6. Reprezentați în cod complementar numărul -112 pe 8 biți;
7. Reprezentați numărul 121 în baza 8;
8. Reprezentați numărul 121 în baza 16;
9. Efectuați conversia numărului 2309 din baza 10 în baza 2, 8 și
16;
10. Efectuați conversia numărului 1011011 din baza 2 în baza 8,
10 și 16;

33
Algoritmică şi programare

Algoritm
Modalitatea în care are loc prelucrarea efectivă a unor date se exprimă
prin algoritm – ca pași succesivi, repetiții și ramificații prin care se execută
operațiile vizate. Rezolvarea oricărei probleme presupune o metodă și o
execuție în pași a acesteia, spre soluția dorită. Pe scurt, algoritmul reprezintă
o succesiune ordonată de operații (număr finit de pași) care trebuie efectuate
pentru a realiza un anumit scop.
Totodată, algoritmii sunt modalități prin care se exprimă succesiunea
de operații prin care un program pe calculator poate ajunge, pornind de la
datele de intrare furnizate, la rezultatele dorite. Trebuie remarcat că un
algoritm nu exprimă doar operații cu numere ci orice fel de prelucrări (cu
texte, imagini, etc.), având scopul de a ajunge la un rezultat.
Un algoritmul trebuie să dețină următoarele caracteristici:
• caracter discret - este format din mai mulți pași;
• caracter finit - numărul pașilor este limitat;
• caracter determinist – se ajunge la un rezultat;
• caracter realizabil - fiecare operație prevăzută este realizabilă
efectiv;
• caracter universal - nu se aplică unui caz izolat, ci unui număr
de cazuri, care diferă prin datele de intrare.
Reprezentarea algoritmilor
În general, descrierea prelucrărilor prin care se obține soluția unei
probleme date se face fără a se specifica tipurile de date utilizate, ca și cum
acestea ar fi subînțelese. Un algoritm este o înșiruire de operațiuni în care se
utilizează variabile și (eventual) alte prelucrări, indicate prin identificatori
inventariați într-un dicționar sau nomenclator ce specifică rolul variabilelor
sau prelucrărilor respective.
Există diferite metode de reprezentare a algoritmilor:
• într-un limbaj natural (de exemplu, în limba română);
• sub formă de schemă logică (reprezentare grafică).
• în pseudocod.

Istoric, prima modalitate de exprimare a algoritmilor au constituit-o


organigramele – cu avantajul descrierii grafice, intuitive și ușor de urmărit.
Evoluția către programarea și proiectarea structurată a impus exprimarea
prin pseudocod – cu avantajele descrierii concise, modularizate și apropiate
limbajelor de programare.
De exemplu, rezolvarea unei ecuații de gradul I de forma ax + b = 0
presupune pașii:

34
Algoritmică şi programare

i) se verifică dacă a = 0; dacă da – ecuația este degenerată;


ii) se verifică dacă b = 0; dacă da x = 0;
iii) altfel, x = -b/a;

Pentru prelucrări mai complexe, trebuie găsită o reprezentare


explicită, în amănunt, a operațiunilor și pașilor efectuați pentru a rezolva
problema. De exemplu, în cazul de mai sus, al ecuației de gradul I, nu s-a
specificat nimic privind introducerea datelor (valorile pentru a și b) dar și
privind afișarea soluției x (care pentru un calculator nu sunt subînțelese –
așa cum sunt ele pentru un om obișnuit cu rezolvarea problemelor de
matematică).
Programul principal:
#1 citește valorile pentru a și b;
#2 soluționarea ecuației de gradul I;
#3 afișează soluția ecuației.
Detalierea pașilor #1, #2, #3:
#1:
afișează pe rând nou: „Introduceți valoarea variabilei a: ”;
citește de la tastatură: a;
afișează pe rând nou: „Introduceți valoarea variabilei b: ”;
citește de la tastatură: b.
#2:
Dacă a=0 atunci
afișează pe rând nou: „Ecuație degenerată”;
altfel
x = - b/a.
#3:
afișează pe rând nou: „Soluția ecuației este x = ”, x;
afișează pe rând nou: „Pentru reluare lansați din nou programul”.
Exprimarea acțiunilor prin pseudocod este o modalitate eficientă de a
descrie operațiunile ce urmează a fi implementate într-un program dar poate
fi folosită și pentru a descrie orice acțiune complexă specifică activității
umane.

35
Algoritmică şi programare

Scheme logice
Succesiunea etapelor rezolvării unei probleme poate fi descrisă grafic
utilizând simboluri (blocuri standard).

Fig. 5 Simboluri grafice (blocuri standard)

Chiar dacă are mai mult o valoare istorică,


schema logică este un bun start în specificarea
unui algoritm, fiind intuitivă și ușor de constituit.

Blocurile grafice generice sunt prezentate


în Figura 5, cu simbolurile și semnificațiile lor:
• terminatorii (START) și (END)
indică începutul și sfârșitul
prelucrărilor – delimitând astfel
algoritmul descris;
• blocurile (ASSIGN) și (CALL)
indică efectiv prelucrările vizate
prin algoritm – descrise prin
formule sau printr-un subprogram
predefinit ;
• blocurile (READ) și (WRITE)
indică o intrare sau o ieșire de
date (citire sau afișare);
• blocul (SELECTION) este
necesar pentru specificarea
ramificațiilor de decizie;
• blocul (LOOP) – numit ciclu
Fig. 6 Schema logica pentru
sau buclă - implică operațiuni rezolvarea ecuației de gradul I
repetate și este necesar pentru
specificarea operațiilor repetitive.

36
Algoritmică şi programare

Succesiunea operațiilor se indică prin linii și săgeți care leagă


blocurile grafice prezentate, astfel constituind așa-numita schemă logică. Ca
exemplu, se prezintă în Figura 6 organigrama (schema logică) pentru
rezolvarea ecuației de gradul întâi.
Schema logică este clară și intuitivă, indicând riguros atât operațiile
cât și succesiunea lor. Astfel, pentru un număr mare de operații, cu ajutorul
schemei logice, algoritmul devine dificil de reprezentat și de urmărit.
Pseudocod
Atunci când se dorește nu doar descrierea algoritmului ci și
structurarea etapelor de rezolvare a problemei (prin modularizare), este
indicată utilizarea unui limbaj codificat care exprimă operațiunile de
executat și cele de control al fluxului de comenzi (cum sunt decizia,
repetiția).
Modularizarea (adică separarea operațiunilor pe secțiuni, fiecare cu un
scop restrâns și specific) este singura modalitate de abordare a rezolvării
problemelor complexe și cu soluție puțin sau deloc cunoscută. Pe de altă
parte, descrierea algoritmilor prin pseudocod este mult mai compactă decât
schema logică (care necesită mult spațiu pe foaia de scris), este apropiat de
un limbaj de programare, fiind o replică exprimată în limba maternă a
programului structurat.
Ca exemplu, vom considera algoritmul de calculare a ariei și
perimetrului unui dreptunghi cu laturile b (baza) și i (înălțimea).

În limbaj natural, se poate descrie astfel:


Pasul 1: se introduc datele b și i;
Pasul 2: dacă oricare din b sau i este număr negativ sau nul, atunci:
Pasul 2.1: scrie “Date de intrare incorecte”.
Pasul 2.2: stop.
Pasul 3: Calculează A=b*i;
Pasul 4: Calculează p=2*(b+i);
Pasul 5: Scrie “Aria dreptunghiului este”, A
Pasul 6: Scrie “Perimetrul dreptunghiului este”, p
Pasul 7: Stop

Același algoritm, reprezentat prin schemă logică. Operațiile sunt


reprezentate prin simboluri grafice, iar succesiunea lor prin linii sau săgeți.

37
Algoritmică şi programare

Fig. 7 Reprezentarea algoritmului prin schemă logică


Același algoritm, reprezentat în pseudocod (un limbaj special de
reprezentare a algoritmilor):
start
citește b, i;
dacă (b<=0 sau i<=0)
atunci scrie “Date de intrare incorecte”
altfel
A=b*i
P=2*(b+i)
scrie “Aria este “,A, “perimetrul este ”,P
stop

38
Algoritmică şi programare

Remarcăm că, în toate cazurile, algoritmul indică ce operații se


execută și în ce ordine, fără să indice de ce se execută (în ce scop).
Limbajul natural nu este suficient de riguros.
Schema logică după cum am menționat anterior, este clară și intuitivă,
indicând riguros atât operațiile cât și succesiunea lor. Totuși, pentru număr
mare de operații, devine dificil de reprezentat și de urmărit.
Pseudocodul este riguros și nu necesită desene. Operațiile sunt
specificate prin cuvinte cheie (de ex. citește, scrie, daca, altfel, cât timp,
stop, etc.).
Elaborarea algoritmilor
Rezolvarea diferitor probleme din lumea reală cu ajutorul
calculatorului se poate face doar dacă soluția se cunoaște dar trebuie căutată
într-un set existent sau dacă există o metodă de găsire a soluției. Algoritmul
este o asemenea metodă, dar elaborarea sa este de cele mai multe ori
dificilă, considerată uneori (îndeosebi la începuturile programării) o artă.
Este evident că folosirea unor metode sistematice de elaborare a algoritmilor
este mai eficientă decât o căutare „la ureche” sau prin încercări; aceste
metode sunt totodată algoritmi generici ce vor fi concretizați apoi pentru
problema reală dată.
Între metodele de elaborare a algoritmilor se amintesc cele mai
importante (numele fiind indicat în engleză spre a fi ușor de recunoscut), cu
o scurtă descriere a specificului lor:
• Greedy – pentru crearea de submulțimi optimale cu elementele
preluate dintr-o mulțime dată și cu respectarea unor restricții impuse
individual elementelor.
• Backtracking – pentru crearea de submulțimi optimale cu elementele
preluate dintr-o mulțime dată, cu respectarea unor restricții impuse
elementelor dar și setului (există relații între elementele submulțimii
soluție).
• Divide et Impera – pentru probleme ce conțin secvențe sau piese
discrete ordonate, care pot fi divizate în subprobleme care se rezolvă
separat și apoi se obține soluția prin combinarea soluțiilor parțiale.
• Branch and Bound – pentru probleme în care soluțiile se pot
reprezenta ca noduri în arbore iar căutarea soluției se face prin
parcurgerea arborelui urmărind totodată o funcție de cost pentru a
limita adâncimea de căutare.
• Programare dinamica
• Metode euristice

39
Algoritmică şi programare

Algoritmii generali pot sta la baza elaborării de algoritmi particulari,


pentru rezolvarea problemelor concrete, după stabilirea celei mai adecvate
metode pentru situația impusă.
Analiza și complexitatea algoritmilor
După cum bine știm, rezolvarea unei probleme se poate face prin mai
multe căi – deci prin algoritmi diferiți, iar în cele mai multe cazuri este utilă,
chiar necesară uneori, compararea acestor căi pentru a răspunde la
următoarele întrebări:
• Soluția obținută este corectă? Dacă da este ea optimă?
• Rezolvarea problemei este inteligibilă și ușor de modificat?
• Execuția algoritmului este eficientă - în sensul timpului de calcul și a
resurselor necesare?
Pe lângă aceste probleme fundamentale din punct de vedere practic
apar și chestiuni teoretice prin care algoritmii se pot analiza și compara:
• Durata de execuție este predictibilă?
• Cât de complexă este rezolvarea problemei relativ la timpul de calcul
necesar?
Complexitatea algoritmului
În acest paragraf se vor stabile mărimi de evaluare a complexității
algoritmilor. Analiza algoritmilor privește în principal timpul de execuție,
dar acesta depinde puternic de sistemul de calcul și de contextul de
rezolvare. O modalitate mai generală este calculul numărului de operații
executate de algoritm pentru rezolvarea problemei – aceasta fiind
dependentă doar de metoda de rezolvare și de numărul n al pieselor de date
de intrare. În acest sens, compararea programelor se poate face urmărind în
principal numărul de operațiuni costisitoare ca timp de execuție, cum sunt:
apeluri de funcții, înmulțiri și împărțiri, atribuiri, comparații, etc.
Pentru un număr n de piese de date la intrare, ne interesează:
• complexitatea temporală – notată T(n), care reprezintă numărul de
operații executate de algoritm asupra setului de intrare (considerând
o operație în unitatea de timp);
• complexitatea spațială – notată S(n), care reprezintă numărul de
locații de memorie folosite de algoritm (memoria de date, stiva,
regiștrii);
unde T(n) și S(n) nu sunt funcții ci relații, fiindcă pot avea pentru
același n rezultate diferite (dependente de modul în care algoritmi diferiți
rezolvă o problemă).

40
Algoritmică şi programare

Analiza unui algoritm se face pentru un număr generic de n date în


cele trei cazuri de interes:
• cel mai defavorabil – indicând maximul numărului de operații
efectuate,
• mediu – indicând media numărului de operații efectuate,
• cel mai favorabil - indicând minimul numărului de operații efectuate.

Uzual, se analizează cazul cel mai defavorabil, apreciat prin Ordinul


algoritmului, notat O (O mare), acesta corespunzând timpului cel mai lung
(sau spațiului cel mai mare) necesar algoritmului pentru a prelucra toate
piesele n de date.
Complexitatea algoritmului este indicată de ordinul său. În urma
analizei unui algoritm rezultă un ordin al acestuia, care poate fi încadrat în
una din clasele de complexitate de mai jos în scopul comparării la un nivel
mai general a algoritmilor:
• Clasa P pentru probleme polinomiale, ce se pot rezolva într-un
număr de operații exprimabil printr-un polinom de n – numărul de
piese de date, într-un mod determinist (adică cunoscut și stabilit
perfect privind operațiile și rezultatul lor).
• Clasa NP pentru probleme nedeterminist polinomiale, ce se pot
rezolva într-un timp polinomial într-un mod nedeterminist (de
exemplu prin „ghicirea” soluției folosind algoritmi genetici, apoi se
verifică dacă soluția este corectă.

În clasa NP există o subclasă de probleme numite NP-complete care


nu se pot rezolva în timp polinomial dar măcar se poate verifica în timp
polinomial o eventuală soluție. Soluția propriu-zisă necesită un timp de
rezolvare cuprins între unul exprimat polinomial și unul exprimat
exponențial.

41
Algoritmică şi programare

Prelucrarea informației
În această secțiune trecem în revistă noțiuni privind operațiunile
efectuate asupra datelor și modul în care se specifică desfășurarea acestora –
conform prelucrărilor vizate. Abordarea sistematică a operațiunilor și
desfășurării lor este impusă de exprimarea concisă și completă a acestora
printr-un limbaj de programare; întrucât limbajele de programare moderne
reprezintă de fapt codificări optime ale operațiunilor necesare oricăror
categorii de prelucrări, se utilizează pe parcursul modulului formalismul
specific acestor codificări.
Identificator, variabilă, constantă, literal
Datele sunt memorate ca succesiuni de biți în memoria de lucru
(RAM) sau pe suport extern (disc magnetic sau optic, etc.), iar ele pot fi
piese simple sau pot fi colecții de piese, cu o semnificație anume (litere,
numere, texte, sunete, imagini). Pentru a fi manipulate, datele trebuie
referite, adică apelate după un nume sau prin adresa acestora în memorie.
Fie prelucrarea algebrică următoare:
a = b + pi + 15 + sin(c)
unde a, b, c și pi, sunt date denumite prin litere, sin() este numele
funcției trigonometrice sinus iar 15 este o valoare imediată. Pe acest
exemplu simplu se vor enunța noțiunile ce urmează.
Identificatorul este numele atașat unei piese sau colecții de date, dar
și a unei prelucrări anume, în general aflate în memoria de lucru.
Numele este un șir de caractere (litere, cifre și alte simboluri de
scriere) care trebuie să satisfacă anumite criterii, funcție de locul de utilizare
a identificatorului. Astfel, în limbajele de programare identificatorii nu pot
conține spatii (constituie un singur cuvânt). În expresia de mai sus
identificatori pentru date sunt a, b, c, pi, iar pentru prelucrări este sin
(calcului sinusului de unghi c). Dacă datele sunt piese sau colecții care se
stochează în memoria internă (RAM) atunci identificatorii au semnificația
unor adrese – prin care se pot manipula variabile și constante.
Variabila este o locație de memorie referită printr-un identificator și
poate conține diverse valori ale unei date de tip specificat.
Valorile pe care le poate lua variabila aparțin domeniului specific
tipului de date respectiv. De exemplu, datele de tip logic pot lua doar două
valori - Adevărat sau Fals, iar datele de tip întreg pe un octet pot lua valori
numere între 0 și 255. Datele vehiculate într-un program sunt de fapt
variabile, care au primit fiecare o valoare chiar la momentul declarației (prin
inițializare) –sau ca rezultat al unei expresii (prin atribuire).

42
Algoritmică şi programare

Constanta este o locație de memorie referită printr-un identificator și


poate conține doar o singură valoare a unei date de tip specificat.
Constantele sunt utile pentru anumite valori speciale (celebre) cum
sunt numărul π sau numărul lui Neper e, constante universale sau doar
valori care nu se doresc a fi modificate în cadrul unor prelucrări (voit sau
din eroare). Constanta este de fapt o variabilă cu restricție de modificare a
valorii, valoarea însăși fiind „ascunsă” după identificator. Identificatorii
constantelor sunt în general nume rezervate - într-un limbaj de programare
(de exemplu pi).
Literalul este o valoare concretă pentru un tip de date, indicată prin
simboluri specifice tipului respectiv.
Inițializarea variabilelor (adică atribuirea unei valori inițiale în tipul de
date propriu variabilei) sau specificarea unei valori imediate într-o expresie
se face utilizând literali. În expresia de mai sus apare banal 15 - ca literal
numeric, dar pentru alte tipuri de date specificarea este mai puțin comună;
de exemplu un literal caracter (litera a) se indică ‘a’.
Expresii
Termenul calculator ne îndreaptă gândul către calcule – posibile fiind
prelucrările pe care acest instrument le poate executa dar, așa cum s-a arătat
anterior, posibilitățile de prelucrare ale calculatoarelor sunt mult mai largi.
Expresia este descrierea formală a unui set de acțiuni efectuate cu un
anumit tip de date. Expresia este o structură sintactică formată din variabile,
literali, operatori și invocări de funcții, care în procesul de calcul este
evaluată pentru a se obține o valoare.
Trebuie avut în vedere că, în programare:
• Expresia este de fapt o secvență de program, în care se indică
ordinea în care trebuie efectuată o succesiune de operații asupra
unor date, în scopul de a obține o valoare.
• Operațiile se fac întotdeauna asupra valorilor.
• Operațiile sunt indicate prin operatori sau/și prin funcții, iar
datele sunt indicate prin variabile și literali. Este, deci, foarte
important să știm nu numai ce operații se fac, ci și în ce ordine se
execută ele.
Semnul = are rolul de a „încărca” în variabila din stânga sa, valoarea
rezultatului obținut în urma evaluării expresiei din dreapta sa. Astfel,
semnul = poate fi privit ca un operator de atribuire, iar întreaga construcție
E = m*c2 ca o expresie cu o singură valoare – anume valoarea obținută
pentru energia E.

43
Algoritmică şi programare

Operatori
Operatorii sunt simboluri ale unor acțiuni cu semnificație stabilită prin
tipul de date operat. Am folosit deja simbolul + ca operator de concatenare
(deci simbol al operației de concatenare). Operatorul poate fi format din
unul sau mai multe caractere. Entitatea asupra căreia se aplică operatorul se
numește operand. După numărul de operanzi, operatorii pot fi:
• operatori unari, care se aplica unui singur operand, de ex. operatorul
”-” in expresia ”-x”; utilizarea operatorului unar se face, de regula,
sub forma "prefix" operator operand (operatorul este pus în fața
operandului) iar uneori sub forma "postfix" operand operator, în care
operatorul este pus după operand;
• operatori binari, care se aplică asupra a doi operanzi, fiind situat
între aceștia; de exemplu operatorul de concatenare ”+” in expresia
"acesta este"+" un exemplu" sau operatorul de adunare a doua
numere ”+” în expresia 17+28. Operatorii binari se folosesc sub
forma "infix" operand1 operator operand2 - în care operatorul este
situat între cei doi operanzi;
• operatori ternari, care se aplica asupra a trei operanzi; de exemplu
în limbajele C și Java există operatorul ternar (? : ) folosit în
expresiile condiționale. Operatorul ternar se scrie sub forma
(operand1 ? operand2 : operand3 ) în care operand1 are o valoare
logica (true sau false), iar ceilalți doi operanzi reprezintă acțiuni.

Pentru o privire completă asupra domeniului, se prezintă în continuare


categorii de operatori, cu simboluri uzuale pentru limbajele C și Java.
Operatori aritmetici sunt operatorii utilizați pentru calculele numerice
și care se aplică funcție de tipul operanzilor (întregi sau reali). Există o
ordine de precedență a operatorilor aritmetici: primii se aplică operatorii
unari apoi cei de înmulțire și împărțire, ultimii de sumare algebrică; această
ordine poate fi schimbată prin grupări între paranteze ‘(’ și ‘)’ ale
operanzilor și operațiilor în expresie.
Operatori relaționali sunt operatori (binari) pentru comparație sau
incluziune: <, >, = =, != (simbol pentru ≠), <= și >= (simboluri pentru ≤ și
≥), in (pentru apartenență∈).
Operatori logici sunt operatorii care produc un rezultat logic
(„adevărat ” sau „fals”). Ordinea de precedență este: primul negarea ! (NU),
apoi conjuncția && (ŞI), ultimul disjuncția || (SAU); ordinea de precedență
se poate schimba prin grupare cu paranteze.

44
Algoritmică şi programare

Tipuri de expresii
Prin rolul său în program, o expresie este o linie de cod care poate fi
redusă la o singură valoare. Se prezintă în continuare tipuri de expresii, prin
exemple, pentru cazuri de operatori și precedență care prezintă interes.
Expresii matematice
Fie declarațiile de variabile:
int a=3, b=7, c=11, d;
care inițializate cu valorile de mai sus, pot fi utilizate în expresii
similare celei de mai jos:
d=(a + c) * b - (a + b)

Expresii logice
Aceste expresii au rezultat logic (adevărat - true, sau fals - false).
Operatorii relaționali dau ca rezultat o valoare de tip logic fiindcă în
urma relației (comparației) rezultatul poate fi adevărat sau fals. Comparațiile
realizate cu operatori relaționali precum și agregările de operatori logici sunt
privite ca întrebări „este adevărat că … ?”

Evaluarea expresiilor
Evaluarea expresiei constă în aplicarea succesivă a operatorilor
asupra operanzilor corespunzători, într-o ordine care depinde de precedența
operatorilor.

Fig. 8 Precedența operatorilor

45
Algoritmică şi programare

Se pot da reguli riguroase de evaluare a expresiilor, dar sunt destul de


complicate. Preferăm să indicăm numai că expresia se parcurge de la
stânga la dreapta, aplicând operatorii în ordinea lor de precedență și luând
în considerație parantezele. Operațiile se fac asupra valorilor operanzilor.
Variabilele se înlocuiesc prin valorile lor.
Imediat ce s-a aplicat un operator, subexpresia respectivă (formată din
operator și operandul sau operanzii corespunzători) se înlocuiește cu
valoarea rezultată din calcul. Se continuă astfel, până când întreaga expresie
este înlocuită printr-o singură valoare.
Precedența poate fi modificată prin utilizarea de paranteze, cu
convenția că operatorii din interiorul unei paranteze se aplică înaintea celor
din exterior. De exemplu, în expresia „a + b*c” operatorul „*” se va aplica
înaintea operatorului „+”, deoarece are precedența superioară. În consecință,
aceasta expresie se va calcula ca și când ar fi scrisă sub forma a + (b * c).

46
Algoritmică şi programare

Instrucțiuni
Pentru prelucrările simple – de genul calculelor matematice, este
suficient un calculator științific, de buzunar. Dacă aceste calcule sunt
combinate pentru a determina un rezultat al unei probleme care implică un
anumit grad de dificultate – de exemplu soluționarea generală a unei ecuații
de gradul întâi, atunci sunt necesare acțiuni de decizie: de exemplu „este
coeficientul necunoscutei nul?” – atunci ecuația este degenerată; asemenea
acțiuni de control a desfășurării prelucrărilor propriu-zise trebuie realizate
printr-un program de calculator, pe baza unor comenzi specifice limbajului
de programare ales.
De fapt, pe lângă prelucrările variabilelor (care se realizează prin
acțiuni secvențiale), mai sunt necesare două tipuri generice de acțiuni: de
decizie și de repetiție. Rezultă că un limbaj de programare trebuie să asigure
comenzi de:
a) Atribuire –obținerea unui rezultat pentru o expresie și
încărcarea valorii acestui rezultat în variabila destinație (din
stânga semnului =).
b) Decizie – pentru alegerea unei secvențe de comenzi din mai
multe variante (uzual două) urmare a rezultatului unei expresii
logice – condiția de decizie.
c) Repetiție – pentru repetarea unei secvențe de comenzi de mai
multe ori, până la îndeplinirea unei condiții de oprire.
Între aceste acțiuni doar atribuirea este cea care privește formularea
expresiilor, rezolvarea și păstrarea rezultatelor. Decizia și repetiția modifică
doar cursul de execuție a atribuirilor. Tabloul comenzilor posibile se
completează cu:
d) Salturi și reveniri – pentru întreruperea unei secvențe de
comenzi și preluarea controlului de către altă secvență de
comenzi în cadrul aceluiași bloc program sau într-un
subprogram.
e) Operații de intrare/ieșire – pentru interacțiunea cu exteriorul,
prin periferice de intrare / ieșire (către om sau instalații, pentru
stocarea sau transferul datelor).

Comenzile din categoriile a) și e) modifică valoarea sau suportul


datelor, iar comenzile din categoriile b) c) și d) modifică controlul asupra
fluxul de comenzi. Uzual, operațiile de intrare / ieșire nu fac parte din
limbajul de programare ci din biblioteci de funcții atașate acestuia.
Cuvintele cheie care exprimă categoriile de acțiuni de mai sus precum
și modul corect de exprimare al lor (adică sintaxa) constituie setul de

47
Algoritmică şi programare

instrucțiuni a unui limbaj de programare. Trebuie remarcat că, în limbajele


de programare moderne, operațiile de intrare - ieșire nu constituie
instrucțiuni de limbaj ci sunt realizate prin subprograme specializate – în
afara limbajului.
Un program este un text ce cuprinde o secvență de instrucțiuni din
categoriile enumerate mai sus, care sunt executate în ordinea în care apar în
text. Procesorul asigură preluarea și executarea comenzilor în secvență,
aceasta fiind ideea centrală de funcționare a mașinilor von Neumann.
Sintetizând cele enunțate anterior, putem afirma că programul este un
ansamblu de instrucțiuni, scrise într-un limbaj de programare, pe care
calculatorul le execută pentru a îndeplini o anumită sarcină.
După cum se poate imagina, comparativ cu instrucțiunile secvențiale,
instrucțiunile de decizie și de repetiție au o exprimare mai complexă – de
exemplu pentru decizie trebuie indicate acțiunile pentru ramura în care
condiția de decizie are rezultat „fals” sau acțiunile pentru ramura cu rezultat
„adevărat”. Instrucțiunile pentru decizii și repetiții sunt mai complexe și au
nevoie de mai multe linii de text pentru a le descrie, pe când pentru
instrucțiunile de atribuire este suficientă o linie de text ce descrie expresia
vizată. De aceea instrucțiunile din prima categorie se consideră „instrucțiuni
simple”, iar cele din ultimele două categorii „instrucțiuni structurate”.
Instrucțiuni simple (secvențiale)
Într-un program acțiunile sunt descrise prin linii de cod/text, care apoi
pot fi interpretate și executate de către calculator – linie cu linie sau pe
loturi de linii. Descrierea unei acțiuni complete se încheie cu
simbolul”;”(punct și virgulă) indicând „sfârșitul acțiunii”. Declarațiile de
variabile sunt instrucțiuni în care se declară tipul și, eventual, valoarea
inițială a unor variabile (inițializare).
Instrucțiunile expresie sunt expresii de atribuire, de incrementare/
decrementare sau de invocare de metodă, care se termină prin simbolul ';'
(punct și virgulă), numită și instrucțiunea vidă.
Instrucțiunea de atribuire
Prelucrarea efectivă a datelor are loc în cadrul evaluării instrucțiunii
de atribuire: una sau mai multe valori intră într-o expresie al cărei rezultat se
atribuie variabilei stânga, simbolul de atribuire = (în limbajele C și Java)
sau := (în limbajul Pascal). Pentru exemplul de calcul al forței:
F=m*a; // în C, respectiv
F:=m*a; /* în Pascal
În expresie intervin numai variabile ce au primit valori, iar variabila
stânga trebuie să aibă tipul de date reprezentând rezultatul expresiei.

48
Algoritmică şi programare

Instrucțiuni de salt
Întreruperea forțată a executării unei secvențe de instrucțiuni (și saltul
la începutul altei secvențe) se poate face condiționat (dacă a fost îndeplinită
o condiție logică) sau necondiționat.
a) Saltul necondiționat – provoacă părăsirea execuției secvențiale a
comenzilor. „Ruperea” secvențelor de comenzi din program prin
salturi necondiționate, vădesc o proiectare defectuoasă,
nesistematizată, a descrierii prelucrărilor; ele fac programul greu
inteligibil și dificil de depanat sau dezvoltat și de aceea, acestea
trebuie eliminate. În limbajele Pascal și C saltul necondiționat există
doar pentru caracterul său istoric:
GOTO eticheta;
Secvența curentă este întreruptă și execuția se continuă de la eticheta.
Există instrucțiuni de salt care provoacă părăsirea secvenței de comenzi doar
în condiții bine precizate, impuse direct de prelucrări.
b) Saltul de revenire din subprogram la programul apelant:
return [expresie];
unde expresie produce ca rezultat o valoare care este „întoarsă”
(returnată) programului apelant de către subprogram, fiind folosită direct în
expresii. Parantezele drepte [ și ] indică o parte opțională – adică expresie
poate lipsi în situația când nu este necesară o valoare returnată. Saltul de la
programul apelant la programul apelat se face prin însăși numele
subprogramului sau funcției.
Salturile de terminare abruptă a instrucțiunilor structurate sunt impuse
de prelucrări (în cazul repetițiilor sau deciziilor multiple):
c) Întrerupere – încheie instrucțiunea curentă și trece la următoarea:
break [eticheta];

d) Continuare – reia secvența curentă de la începutul ei:


continue [eticheta];
Cazuri de utilizare sunt la întreruperea unei bucle (și saltul în afara sa,
la secvența ce începe de la eticheta), respectiv reluarea unei bucle de la
început, cu/fără parcurgerea întregii secvențe din buclă.
Instrucțiuni structurate (structuri de control)
Structurile de control se mai numesc și instrucțiuni compuse și au
rolul de a indica succesiunea în care se execută instrucțiunile programului.
Distingem următoarele structuri de control: blocul, instrucțiunile de
ramificare (decizia/selecția), instrucțiunile repetitive (bucle sau ciclurile) și
structura de tratare a excepțiilor.
Blocul este o succesiune de instrucțiuni cuprinsă între acolade.
Instrucțiunile din cadrul blocului pot fi simple sau compuse. În particular,

49
Algoritmică şi programare

un bloc poate conține alte blocuri. Variabilele declarate într-un bloc sunt
valabile numai în blocul respectiv, din locul declarării ei până la sfârșitul
blocului, inclusiv în blocurile interioare.
Structurile ramificate (decizia/selecția) sunt structuri de control în
care fluxul programului conține două sau mai multe ramuri paralele. În
limbajele Java si C/C++, structurile ramificate sunt realizate prin
instrucțiunile if, if-else și switch.
Structurile repetitive, numite și bucle sau cicluri, sunt structuri de
control în care o anumită instrucțiune simplă sau compusă, de regulă un
bloc, se execută în mod repetat. În limbajele Java si C/C++, ciclurile se
realizează prin instrucțiunile while, do-while și for.
Controlul fluxului de instrucțiuni este necesar pentru soluționarea
unei probleme complexe, iar algoritmul este de fapt o rețetă de control al
fluxului de instrucțiuni prin care se caută soluția la problema dată.
Instrucțiuni decizionale (de selecție)
Instrucțiunea decizională (if)
Instrucțiunea if este o instrucțiune
condițională, care are forma generală:

if(condiție)
instrucțiune

în care:
condiție - este o expresie logică
instrucțiune - este o instrucțiune simplă sau compusă, care se execută
dacă și numai dacă expresia condiție are valoarea true.

Instrucțiunea decizională binară (if-else)


Pentru indicarea a două alternative în desfășurarea acțiunilor unui
program se folosește decizia sau ramificația binară „dacă .. atunci”.
Instrucțiunea if-else este o instrucțiune condițională, care are forma
generală:

if (expresie)
instrucţiune1;
else
instrucţiune2;

unde instrucţiune1 se execută doar atunci când condiția expresie are


valoare logică „adevărat” (sau în limbajul C/C++, are o valoare diferită de

50
Algoritmică şi programare

0), altfel (când condiția este falsă sau 0) se execută instrucțiune 2. Când pe
una din ramuri sunt mai multe instrucțiuni de executat, atunci se folosește pe
acea ramură instrucțiunea compusă. Se observă că, prin modul de scriere a
textului, ies în evidentă prin „indentare” (adâncire spre dreapta) ce se
execută pe ramura „adevărat” (imediat după if) și ce se execută pe ramura
„fals” (imediat după else).
În cazul în care nu există o instrucțiune 2, atunci ramura else lipsește
și se continuă cu secvența ce urmează după instrucțiunea vidă (simbolul de
sfârșit ;).
În diferite limbaje de programare (inclusiv în limbaje „script”) se
folosește pentru decizia binară construcția de mai sus, cu diferențe minore
de scriere (sintaxă): lipsesc parantezele după condiție și apare cuvântul then
(ca în Pascal) sau în loc de instrucțiunea vidă(;) este folosit endif.
Fie exemplul banal de „algoritm pentru rucsacul de vacanță”:
1) dacă (este vacanța de vară) atunci
2) repetă
adaugă în rucsac tricou;
3) repetă
adaugă în rucsac obiect de plajă;
1’) altfel
4) repetă
adaugă în rucsac pulover;
5) repetă
adaugă în rucsac obiect pentru schi;
6) închide rucsacul;
În acest exemplu se observă „decizia” 1) – 1’) și „repetiția” 2), 3) sau
4), 5). Acestea se vor regăsi ca instrucțiuni într-un limbaj de programare ca
instrucțiuni structurate – remarcați structura deciziei dacă .. atunci .. altfel,
subliniată prin numerotarea cu apostrof. Cuvintele cu litere înclinate
(cursive) indică modul cum se vor efectua operațiunile (sunt „instrucțiuni”)
iar cuvintele scrise normal sunt operațiunile înseși (de fapt instrucțiuni
simple). Astfel, la pasul 2) se repetă așezarea în valiză a tricourilor – unul la
un moment dat (cel roșu, apoi cel verde, etc.), iar la pasul 3) similar,
așezarea fiecărui obiect de plajă (umbrelă, ulei de plajă, etc.). Omul execută
operațiunile înscrise mai sus una după alta, fără a fi necesară indicarea
ordinii lor prin numere – în acest exemplu au fost numerotate doar pentru a
fi referite. Similar, procesorul (construit după principiile enunțate de von
Neumann) execută operațiile una după alta, în ordinea apariției lor în textul
programului; în exemplul nostru, după încheierea repetiției 3) se execută
direct 6) – în cazul vacanței de vară, similar în cazul vacanței de iarnă.

51
Algoritmică şi programare

Instrucțiunea de decizie (selecție) multiplă


Decizia binară – prezentată mai sus, privește situații simple, cu două
alternative: Alb/Negru, Da/Nu, Adevărat/Fals. Pentru situații în care decizia
urmărește mai mult de două alternative, este dificil de aplicat mai multe
instrucțiuni de decizie binară. O asemenea situație apare când expresia de
selecție nu are valori binare ci multiple – cum ar fi cazul selecției opțiunilor
unui meniu; unde, fiecare opțiune devine un caz selectat printr-un număr sau
prin poziția indicatorului pe ecran („mouse”). Ca exemplu, se prezintă
instrucțiunea switch în limbajul C/C++, într-o secvență de program în care
se alege o opțiune din trei posibile la alegerile prezidențiale, prin numărul
acesteia – furnizat prin NumarOptiune.
Instrucțiunea switch este o structură de control, care are forma:

switch (NumarOptiune)
{
case ‘1’: { “Candidat de stânga”};
break;
case ‘2’: { “Candidat de centru”};
break;
case ‘3’: { “Candidat de dreapta”};
break;
default: { „Exprimați-vă opțiunea”};
}

Se observă că pentru un alegător indecis (care alege NumarOptiune


diferit de 1, 2 sau 3) există posibilitatea de a fi atenționat că doar aceste
opțiuni sunt disponibile – prin secțiunea default (care înseamnă „implicit” în
engleză); textele dintre {} la fiecare caz, apar în loc de blocuri program prin
care se afișează – de exemplu, sigla și numele candidatului. Instrucțiunile de
salt break, sunt utilizate pentru a încheia posibilitatea de selecție după ce s-a
exprimat o opțiune, în scopul continuării cu secvența program care urmează
instrucțiunii switch, adică după }.

52
Algoritmică şi programare

Instrucțiuni repetitive (bucle sau cicluri)


Instrucțiunea repetitivă cu număr cunoscut (finit) de
pași
Deseori, prelucrările pentru care este util
calculatorul sunt cele în care se repetă anumite operații
de foarte multe ori; omul ar obosi (apoi greși) la
repetiții îndelungate ale acelorași operații, dar
echipamentul electronic le execută precis, rapid și fără
complicații sociale. Instrucțiunea for, întâlnită și sub
denumirea de ciclul cu inițializare, test și reinițializare,
se folosește în special atunci când numărul de repetări
al corpului ciclului este dinainte cunoscut. Această
instrucțiune are forma:

for(iniţializare opt ; condiţieContinuare opt ; incrementare opt )


instrucţiune opt

în care:
inițializare: listă de expresii de inițializare, separate prin virgule;
condiţieContinuare: expresie logică;
incrementare: listă de expresii, care se execută după executarea
corpului ciclului;
instrucțiune: instrucțiune simplă sau compusă (de preferință un bloc)
care constituie corpul ciclului. Toate componentele marcate cu opt sunt
opționale.
Executarea instrucțiunii for decurge astfel:
• se execută mai întâi secvența de expresii inițializare;
• se intră în ciclu, executându-se în mod repetat instrucțiune
urmată de secvența de expresii incrementare, cât timp expresia
condiţieContinuare are valoarea true;
• când expresia de condiţieContinuare are valoarea false, se iese
din ciclu.

Atunci când se cunoaște numărul de iterații (cuvânt ce indică „repetiții


numerotate”), este utilă instrucțiunea for – ce apare în diverse limbaje, dar
se va prezenta cu sintaxa uzuală în limbajul C/C++. Trebuie remarcat că,
pentru a efectua numărul de iterații dorit, este necesar un contor pentru care
se indică o valoare de start, o valoare de final și o modalitate de avans a
contorului (în această ordine) prin expresiile ce apar între () și sunt separate
cu ; ca mai jos.

53
Algoritmică şi programare

for (NrStud=1,NrBilet=35;NrStud<=30;NrStud++,NrBilet--)
{ „Prezintă legitimație și primește bilet de examen” };

Exemplul privește verificarea legitimației de student și primirea de


către acesta a biletului de examen, la o grupă de 30 studenți. Contorul
NrStud reține câți studenți au intrat în sala de examen iar contorul NrBilet
reține câte bilete au mai rămas examinatorului – din totalul de 35; expresiile
NrStud++ şi NrBilet-- adună și respectiv scad o unitate din contoarele
respective (aceasta înseamnă ++ și respectiv --).
Instrucțiuni repetitive cu test (inițial sau final)
Atunci când nu se cunoaște numărul de iterații, terminarea repetițiilor
poate fi indicată de o condiție cunoscută – care se aplică înainte sau după
efectuarea operației, în bucla de repetiție. Ca urmare, se folosesc una din
cele două tipuri de instrucțiuni:
a) ciclul cu test inițial (while): „cât timp (condiție) adevărată – repetă
{operație}” – prin care este posibil ca operația să nu se execute nici
măcar o dată, dacă de la început condiția nu este îndeplinită;
b) ciclu cu test final (do-while): „repetă {operație} – cât timp (condiție)
adevărată” – prin care operația se execută cel puțin o dată, indiferent
dacă este adevărată condiția la intrarea în buclă.

Fig. 9 Instrucțiuni repetitive cu test (inițial sau final)


Instrucțiunea while realizează structura de control repetitivă numită
ciclu cu test inițial și are următoarea formă:
while(condiție)
instrucțiune

54
Algoritmică şi programare

în care:
condiție - este o expresie logică;
instrucțiune - este o instrucțiune simplă sau compusă (de regulă un
bloc), care se repetă cât timp expresia condiție are valoarea true.
Instrucțiunea do-while realizează structura de control repetitivă
numită ciclu cu test final și are următoarea formă:
do
bloc
while(condiție);
în care:
bloc - este o structură de control sub formă de bloc;
condiție - este o expresie logică.
Executarea blocului se repetă și în acest caz cât timp este satisfăcută
condiția dar, spre deosebire de instrucțiunea while, în acest caz testarea
condiției se face după ce a fost executat corpul ciclului.
Pentru exemplul de mai sus reluat, dar în care nu se cunoaște numărul
de studenți ce se prezintă la examen, descrierea în limbajul C/C++ a celor
două cazuri ar fi:

(a) while (NrStud>0)


{ „Prezintă legitimație și primește bilet de examen” };

(b) do
{ „Prezintă legitimație și primește bilet de examen” };
while (NrStud>0);

Lăsăm cititorul să decidă care dintre formele (a) sau (b) este adecvată
exemplului ales. (Indicație: este posibil ca la un examen să nu se prezinte
nici un student).
Dacă există cazuri în care bucla de repetiție trebuie întreruptă în
desfășurarea ei (de exemplu în cazul când unele operații nu se execută dacă
nu este îndeplinită o condiție), atunci apar în interiorul buclei instrucțiuni de
salt, de tipul break (întrerupe repetiția și părăsește bucla) sau continue (reia
bucla de la început fără executarea operațiunilor care urmează acestei
instrucțiuni).

55
Algoritmică şi programare

Programe și subprograme
Secvențele de instrucțiuni sunt organizate în programe și
subprograme, fiecare având un nume care – în principiu, indică rolul
prelucrărilor acestuia. Program este denumirea generică a unei înșiruiri de
comenzi care execută prelucrări într-un scop dat; comenzile pot fi exprimate
prin cuvinte cheie specifice unui limbaj de programare (în programarea
„clasică”) sau poate fi o structură de reprezentări grafice ale comenzilor,
plasate pe o suprafață de lucru (în programarea „vizuală”). Pe scurt,
Program= date + algoritm.
Programul reprezintă un ansamblu de instrucțiuni, scrise într-un
limbaj de programare, pe care calculatorul le execută cu scopul de a
îndeplini o anumita sarcina.
Programul conține:
• descrierea datelor de intrare, de ieșire și intermediare
cu care se operează;
• descrierea operațiilor efectuate asupra datelor (a
algoritmului de prelucrare).

În cazul programelor scrise într-un limbaj de programare, structura


textului depinde de modul de programare (structurată sau obiectuală), în
principiu, pentru un program fiind specificate:

nume_program (lista parametri)


{
declarații variabile
corpul programului
}

unde nume_program este un identificator al programului (prin care poate fi


apelat spre a executa acțiunile înscrise în el), lista parametri este setul de
date care se furnizează programului (date de intrare), declarații variabile
indică variabilele (locale) necesare prelucrării/stocării rezultatelor
intermediare, corpul programului este secvența efectivă de comenzi pentru
acțiunile vizate, iar acoladele {} încadrează corpul programului și
delimitează programul propriu-zis.

56
Algoritmică şi programare

Subprograme
În cazul unor prelucrări complexe, care sunt supuse procesului de
ciclare, este indicată folosirea subprogramelor; acestea sunt secțiuni de cod
scrise o singură dată și folosite de mai multe ori, prin apelarea lor ori de câte
ori este nevoie, separat sau în cadrul expresiilor. Apelarea subprogramului
se face prin intermediul identificatorului său (numele), similar cu referirea
unei variabile.
Dacă un subprogram este inclus unei biblioteci de subprograme el
poate fi apelat de către oricare alt program scris în limbajul respectiv sau în
alte limbaje (dacă la apelare se fac precizările de rigoare). Subprogramul
este o prelucrare care se poate efectua asupra unui set de date ale căror
valori sunt specificate la „apelarea” subprogramului.
Subprogramul este un program apelat (prin nume) în cadrul altui
program (programul apelant) pentru a executa acțiunile sale specifice. Deci
subprogramele nu sunt „de sine stătătoare”, adică nu pot fi lansate direct
de către utilizator din sistemul de operare.
Subprogramul care, după execuția acțiunilor sale, revine în programul
apelant cu o singură valoare (valoare „returnată”) ce poate fi folosită direct
în calculul unei expresii; se numește funcție. Un exemplu clasic este funcția
sin(x), care poate fi apelată într-o expresie – de exemplu a+b+sin(x), sumele
fiind realizate doar după furnizarea calculului sinusului pentru valoarea x.
Structura textului de definire a unei funcții adaugă, la structura de principiu,
specificarea tipului valorii returnate și comanda de revenire în programul
apelant:

tip_valoare_returnata nume_functie (lista parametri)


{
declarații variabile
corpul programului
return expresie; //nu apare obligatoriu la final
}

unde tip_valoare_returnata specifică tipul de date al valorii rezultate în


urma calculelor din expresie și care va fi adusă la revenirea în programul
apelant.
Execuția programelor se face de către procesor, care poate funcționa
doar secvențial și pentru o singură sarcină (un singur program) la un
moment dat. La apelul unui subprogram de către programul apelant, acesta
din urmă trebuie „părăsit” de către procesor iar contextul de lucru (adică
setul de rezultate parțiale) din memoria locală a procesorului trebuie salvat
în memoria internă – RAM. La revenirea din subprogram, acest context se

57
Algoritmică şi programare

încarcă din memoria RAM în memoria locală a procesorului iar programul


apelant reia lucrul – exact din punctul în care a fost întrerupt pentru execuția
subprogramului, folosind valoarea „întoarsă” ca rezultat de către
subprogram (vezi instrucțiunea return), și valoarea rezultat pentru expresie.
Programul principal
Execuția acțiunilor înscrise într-un program trebuie inițiată la
comanda utilizatorului. Pentru aceasta sistemul de operare interacționează
cu omul, primește comanda (care de obicei este chiar numele programului)
și „lansează” execuția acestuia.
Partea din textul unui program care poate fi lansată nemijlocit de
sistemul de operare (deci care poate funcționa de sine stătător) se numește
program principal. Acesta este, în general, o succesiune de acțiuni grupate
în corpul programului pe trei secțiuni:
i) Introducerea datelor
ii) Prelucrarea datelor
iii) Afișarea rezultatelor
Pentru execuția acestora, se face apel la subprograme din biblioteca de
subprograme sau din setul subprogramelor declarate în textul sursă al
programului ca ansamblu; în acest ultim caz, programul va fi specificat
astfel:
nume_program_principal (lista parametri)
{
declarații variabile
declarații subprograme
definire subprograme
{
corpul programului principal
}
}
unde declarații subprograme reprezintă secțiunea în care se
inventariază numele și lista parametrilor, specifice tuturor subprogramelor
apelate în textul programul. Secțiunea definire subprograme (declarația
forward) reia declararea subprogramelor, de această dată cu descrierea
acțiunilor din fiecare subprogram.

58
Algoritmică şi programare

Realizarea programelor și programe suport


Funcționarea unui sistem de calcul presupune existenta pe suportul
fizic (echipament) a secvenței de comenzi (program) care indică ce
prelucrări suportă valorile de interes (date). Anterior am nuanțat cele trei
entități, privind:
(I) structura constructivă și funcțională a echipamentului de
calcul;
(II) reprezentarea datelor referitoare la diverse tipuri de
informații;
(III) comenzile elementare (instrucțiuni) și modalitățile de
soluționare generică a problemelor (algoritmi).
Totuși, acestea sunt doar instrumente puse la dispoziția omului de
Tehnologia Informației și Comunicațiilor dar, rezolvarea completă a unei
probleme noi (necunoscute) presupune abordarea tuturor aspectelor ce apar
în situații reale, ba chiar mai mult, elaborarea unei soluții generice pentru
toate (sau cât mai multe din) problemele similare.
Sunt necesare:
(IV) analiza problemei (sau clasei de probleme) și elaborarea unei
soluții generale adecvate (analiza și soluționarea problemei);
(V) înscrierea comenzilor de găsire a soluției cu prevederea
tuturor situațiilor ce pot apare, apoi furnizarea acestora
calculatorului (proiectarea și realizarea programului);
(VI) validarea soluției și utilizarea ei pentru situații concrete din
clasa de probleme .

În acest modul se vor aborda chestiuni ce privesc acțiunile (IV), (V) și


(VI), cu metodele sistematice ce stau la baza lor, cu mijloacele prin care
aceste acțiuni se pot realiza eficient (sau automat) și cu chestiuni legate
indirect de utilizarea soluției – cum sunt documentarea, întreținerea
sistemului, dezvoltarea sa. În principal, scopul tuturor acestor acțiuni este
realizarea programului de calculator, adică programarea.
Programul este o secvență de comenzi exprimată într-un mod
codificat și care poate fi interpretat și executat de către calculator, având ca
scop prelucrarea informației.
După cum s-a arătat, un program nu prezintă doar secvențe
(succesiuni) de comenzi ci și ramificații sau repetiții iar secvența este
esențială fiindcă reflectă modalitatea în care omul concepe rezolvarea unei
probleme – pași succesivi, cu o singură operație la un moment dat. Chiar și
procesorul este astfel realizat constructiv (indiferent cât de perfecționat ar fi)
pentru a executa o singură comandă al un moment dat sau, mai multe
comenzi în serie (secvență). Un sistem de calcul poate executa mai multe

59
Algoritmică şi programare

comenzi simultan doar dacă are mai multe procesoare (sistem multi-
procesor, multi-core sau mașină paralelă).
Pe de altă parte, la rezolvarea unei probleme cu ajutorul calculatorului
apar situații complexe, colaterale problemei de bază, ce trebuie și acestea
rezolvate, cum sunt: condiții limitate de către contextul de lucru sau de
valorile de intrare, greșeli posibile pe care operatorul uman le poate face
(din necunoaștere sau neatenție); apoi prezentarea rezultatelor este
importantă: tabele, grafice, sunete, etc.
Se constată deci că rezolvarea unei probleme (chiar foarte simplă)
implică de fapt rezolvarea altor multiple probleme privind utilizarea de către
om a soluției.
Aplicația este un set de programe reunite și interdependente, care se
prezintă într-un mod unitar și oferă soluții pentru o clasă de probleme date.
Problematica programării
Programarea este un proces de exprimare a instrucțiunilor prin care
se rezolvă o problemă și care pot fi convertite mecanic în acțiuni ale unui
sistem de calcul.
Programarea este dependentă de limbajul de codificare a
instrucțiunilor, de cunoștințele programatorului privind regulile structurale
(sintaxa) și semnificația elementelor de limbaj (semantica), de modul cum
se execută (clar, comentat și documentat) și de modul general de gândire
privind soluționarea problemelor (metode de analiză și implementare). De
aceea, o lungă perioadă, programarea a fost considerată o „artă”, unde
programatorul s-a putut exprima pe sine (ca mod de gândire, eleganță în
exprimare, eficientă a soluției) prin produsul program realizat. Întrucât
informatica a devenit o industrie, programarea este privită astăzi prin prisma
eficienței muncii și a soluțiilor, adică vizează productivitatea muncii de
programare și o standardizare a utilizării produselor.
Rezultatul programării este un produs program. Ca orice produs, el
are un ciclu de fabricație, o valoare de utilizare (și de aici un preț), precum
și un proprietar. Spre deosebire de alte produse, un program produs
dezvoltat de o firmă este proprietatea acesteia și nu a cumpărătorului
programului; cumpărătorul achiziționând doar dreptul de utilizare a
programului și ca atare nu îl poate modifica sau revinde. Un program
reprezintă implementarea unei idei (deci presupune o „tehnologie” de
implementare a ideii) și, conform reglementărilor privind dreptul de
proprietate intelectuală (pentru orice creație), firma sau persoana care l-a
elaborat are drepturi de autor asupra lui, însă nu poate emite pretenții asupra
ideii la care se referă programul. Produsele program intră astfel pe piață,
alături de alte produse, cu specificul lor – nu pot deveni proprietatea
cumpărătorului iar multiplicarea lor neautorizată este ilegală.

60
Algoritmică şi programare

Etape în ciclul de viață ale unui produs program


Programarea propriu-zisă este doar o etapă în realizarea și existenta
unui program. Ciclul de viață al unui produs program cuprinde acțiuni
desfășurate de la lansarea ideii programului până la înlăturarea lui (fiind
perimat sau inutil). Etapele din viața unui program sunt grupate în faze ca în
Figura 10 iar evoluția ciclică a fazelor este ilustrată în
În continuare sunt descrise etapele din ciclul de viață, indicând
metodologii sau abordări sistematice care cresc eficienta muncii în fiecare
etapă. O metodologie în domeniul analizei și proiectării unei aplicații (sau a
unui sistem informatic) se referă la următoarele aspecte:
• modele – ca viziuni conceptuale asupra obiectelor și activităților într-
un domeniu dat,
• metode – ca acțiuni ce determină obiecte și activități concrete din
problema de rezolvat,
• instrumente – ca mijloace de lucru software care ajută analiza și/sau
proiectarea aplicației.

Fig. 10 Faze și etape în existența unui produs program

61
Algoritmică şi programare

Formularea cerințelor
În această etapă se enunță de fapt problema ce se dorește rezolvată cu
ajutorul calculatorului și constituie motivul pentru care este necesară
aplicația. Se enunță o soluție de principiu, care rezultă chiar din formularea
cerințelor noului program sau sistem informatic. Adesea, beneficiarul
aplicației nu știe exact ce dorește de fapt și nici posibilitățile pe care un
program i le poate oferi. De aceea, ciclul de viață se poate relua, chiar cu
reformularea cerințelor – spre a fi în acord cu realitatea sau cu disponibilul
financiar pentru produsul program.

Fig. 11 Ciclul de realizare a unui produs program (aplicație sau sistem informatic)
Analiza problemei
Această etapă începe cu „Studiu și elaborarea soluției problemei”, prin
care se evaluează situația existentă, se parcurg metode sau soluții deja
aplicate în situații similare, apoi se stabilesc acele metode (eventual și
algoritmii) care permit rezolvarea conceptuală a problemei. Studiul efectuat
descrie problema (sau sistemul țintă) din următoarele puncte de vedere:
i) Viziunea externă (specificația) – asupra scopurilor aplicației;
ii) Viziunea organizațională (structurală) – asupra modului de
realizare a aplicației;
iii) Viziunea comportamentală (temporală) – asupra evoluției
dinamice a aplicației;
iv) Viziunea asupra resurselor – hardware (echipamente de
prelucrare și transfer), software (alte programe necesare

62
Algoritmică şi programare

aplicației), resurse umane (implicate în operarea și utilizarea


aplicației), resurse financiare (sume estimate pentru
realizarea aplicației).
Pentru realizarea unei analize precise și complete, urmată de
elaborarea sistematică a soluției, se recomandă respectarea unei metodologii
de analiză, care este, de obicei, specifică modului de proiectare și dezvoltare
a aplicației. La baza celor mai multe metodologii stă conceptul de diagramă
„Entitate-Relație”, care constă într-o reprezentare grafică, intuitivă, a
obiectelor și legăturilor dintre ele în problema reală dată. De asemenea,
abordările de analiză pot evolua „de la mic la mare” (bottom-up, de jos în
sus, de la amănunt la general) sau „de la mare la mic” (top-down, de sus în
jos, de la general la amănunt).
Fiecare metodologie deține instrumente software adecvate, pentru
asistarea experților umani în modelarea soluției și a programului sau
sistemului de informatizare. Prin aceste instrumente se pot elabora
sistematic structuri de obiecte conceptuale, care se reprezintă ca diagrame și
scheme bloc fizice și funcționale ale viitoarei aplicații.
Persoanele implicate în această etapă: analiști în domeniul problemei
(adică specialiști cu experiență și suficiente cunoștințe pentru a elabora o
soluție viabilă și în detaliu), analiști de sistem (adică informaticieni cu
experiență în tipul de probleme din care face parte problema de rezolvat),
beneficiarul și utilizatori obișnuiți pentru sistemul existent (care dau detalii
asupra situației existente și problemei de rezolvat, pretind un mod de
funcționare a aplicației și un anume mod de prezentare a rezultatelor).
Documentele care rezultă din această etapă sunt „Specificația de proiectare”
(descrierea de principiu a informațiilor și prelucrărilor) și „Strategia de
testare” (care prevede modurile în care se vor testa modulele și întreg
ansamblul, precum și datele de test – cu rezultatele așteptate).
Proiectarea aplicației
Etapa de proiectare („design”) se referă la structurarea efectivă a
blocurilor software cu indicarea rolurilor, interacțiunilor și resurselor
fiecăruia. Activitatea de proiectare implică abstractizarea faptelor ce au
rezultat în etapa de analiză, pentru modelarea informațiilor și acțiunilor
necesare rezolvării problemei date. Procedura de abstractizare elimină
faptele irelevante și le accentuează pe cele esențiale, iar procedura de
modelare reprezintă informații și acțiuni într-un mod specific. Modelul
obținut privește doar aspectele care se doresc rezolvate ale problemei (nu
toate aspectele acesteia) și va conține obiecte cu (și asupra cărora) se
acționează. Pentru un produs informatic, modelul poate fi formal (adică
exprimat prin simboluri, de exemplu prin formule) sau procedural (adică
exprimat prin cuvinte ca o rețetă de bucătărie).

63
Algoritmică şi programare

Metodologia aplicată la etapa de proiectare este puternic dependentă


de modalitatea de programare. De aceea, etapele de proiectare și
implementare sunt strâns legate, uneori chiar suprapuse iar această legătură
provine din modul cum este gândită, chiar de la etapa de proiectare,
realizarea efectivă (implementarea) aplicației pe întregul ei și pe fiecare
program în parte. Între metodologii se amintesc două mai importante:
proiectarea obiectuală (pentru aplicații în care se pot discrimina obiecte din
lumea reală ce sunt manipulate de aplicație) – cu utilizare mai frecventă în
domenii tehnice și care simulează realitatea și proiectarea cu baze de date
(pentru aplicații de gestiune a resurselor de orice fel) – cu utilizare frecventă
în economie și administrație.
Pe lângă partea software, la această etapă se proiectează și structura
de echipament, privind: structura de calculatoare și configurația fiecăruia,
structura de comunicație (rețea locală, echipamente de rețea, conectarea la
Internet) și structura de periferice partajate (adică folosite în comun) de mai
mulți utilizatori (imprimante sau mese de desen, interfețe de proces pentru
culegerea datelor sau comanda din/către instalații). Se proiectează tipul și
configurația sistemelor de operare - strâns legat de structura de echipamente
și de scopurile aplicației.
Persoanele implicate în această etapă sunt: analiști de sistem
(informaticieni cu pregătire specială în folosirea unui instrument de
proiectare și implementare a programelor), ingineri hardware și ingineri de
sistem (care proiectează structura de echipamente și programe), conducători
de proiect (specialiști în domeniul țintă sau în informatică, care cunosc
modul de organizare a activităților complexe precum și domeniul țintă).
Documentele elaborate la finalul etapei sunt „Specificația de programare”
(indică structura de module și acțiuni apoi datele necesare fiecărui program),
„Planificarea lucrărilor de implementare ”și „Inventarul resurselor necesare”
(financiare, umane și materiale) pentru realizarea noului program sau sistem
de informatizare,
Implementarea și testarea aplicației
Activitatea esențială a acestei etape este programarea. Se vorbește
adesea de „programarea calculatoarelor” subînțelegând toate activitățile
implicate de aceasta, poate fiindcă programarea este activitatea prin care
efectiv echipamentul de calcul devine funcțional (fără programe este „doar
fier”). În sine, programarea constă în codificarea operațiunilor pe care
calculatorul trebuie să le execute către atingerea unui scop dat (calcul
matematic, retușarea și afișarea unei imagini, sau mișcarea brațului unui
robot). După cum se constată, programarea este doar partea de realizare
efectivă a programului, care însă necesită multe alte activități anterioare și
posterioare.

64
Algoritmică şi programare

Fazele realizării unui program sunt:


(I) Scrierea programului sursă – prin care se descriu acțiuni (folosind
un limbaj de programare) într-un text scris cu un editor de texte. Atât
limbajul cât și modul de realizare a programului sursă sunt apropiate
obișnuințelor umane (cum spre exemplu, o rețetă de bucătărie este
înscrisă ca text, într-o formă simplificată uneori chiar codificată).
(II) Compilarea – prin care textul sursă este „tradus” din limbajul de
programare (exprimat prin cuvinte – ) în limbajul mașinii (exprimat
prin coduri binare). Traducerea este realizată de un program special
pentru limbajul de programare ales – numit compilator, iar rezultatul
este codul obiect al programului.
(III) Editarea legăturilor – prin care în codul obiect se inserează
subprograme, preluate din biblioteci de subprograme, ce descriu
prelucrări uzuale, pe care programatorul le folosește fără a mai scrie
cod (fără a scrie programul ci doar a-l apela din bibliotecă). Astfel,
prelucrări care au fost doar amintite în programul sursă se înscriu
efectiv în codul obiect. Rezultatul fazei este codul executabil al
programului, adică forma binară ce poate fi încărcată direct în
memoria de lucru și poate executa operațiunile programate.
Cuvintele, ce exprimă comenzi, se combină în limbajul de programare
respectând o sintaxă strictă (ca reguli gramaticale); programatorul poate
greși (din neatenție, din necunoaștere), astfel că textul sursă fiind greșit, este
posibil să nu poată fi interpretat de calculator. În acest caz, este necesară:
(IV) depanarea programului – care constă în modificarea textului
sursă spre a fi eliminate erorile. Identificarea erorilor și apoi
verificarea programului se realizează cu ajutorul unui depanator
(program de asistare a programatorului în activitatea de verificare a
corectitudinii programului). Corectarea efectivă a erorilor constă în
înscrierea corectă a cuvintelor sau a combinațiilor de cuvinte în textul
sursă.

Depanatorul localizează erorile din program şi face chiar sugestii de


corectură, însă aceste erori sunt legate doar de „modul de exprimare” în
limbajul dat, nu de modul cum a fost rezolvată problema (dacă soluția este
corectă sau nu); eliminarea erorilor de soluționare a problemei se poate face
doar prin executarea de teste pe date și în situații reale, urmată de
compararea rezultatelor cu cele așteptate și apoi modificarea algoritmilor de
prelucrare.
În general, editarea, compilarea și depanarea programului se
realizează folosind un mediu integrat (un program complex cu toate aceste
instrumente), spre a spori eficiența muncii de programare. Astfel de
instrumente sunt „mediile de programare” sau „instrumentele de inginerie

65
Algoritmică şi programare

software”. Scrierea efectivă a programului se numește codificare. Această


operațiune complexă nu se realizează doar înscriind textul în limbajul de
programare ales ci se includ date, obiecte sau prelucrări „prefabricate” din
biblioteci ale mediilor de programare utilizate pentru scrierea aplicației.
Aplicația se implementează modular – fiecare subprogram rezultat la
proiectare (și înscris în „Specificația de programare”) este codificat și testat
separat. La realizarea programelor se respectă principii de inginerie a
programării, în scopul depanării facile și apoi a dezvoltării coerente a
fiecărui modul și aplicație. După ce modulele sunt verificate, se face
integrarea aplicației, adică se instalează toate piesele software și hardware
ale aplicației. Se face testarea ansamblul în condiții de laborator și se emit
documentele de conformitate cu cerințele (dacă sunt respectate sau nu, care
din cerințe nu au fost satisfăcute și de ce).

Fig. 12 Fazele de realizare ale unui program


În situația în care funcțiile aplicației sau utilizarea acesteia nu sunt
conforme cerințelor, se decide dacă, și pentru care din cerințe, se reiau
fazele de analiză, proiectare și apoi cele de implementare cu testare.
Similar fazei de proiectare, pentru structura de echipamente se
procedează la achiziționarea, instalarea și testarea fiecărui echipament și a
întregului sistem, format din calculatoare, rețea și echipamente de
interconectare, periferice în rețea, alimentare cu energie electrică, spații de
securizare a echipamentelor sensibile și stocare a suporturilor de date.
Persoanele implicate în aceste faze sunt: analiști programatori
(elaborează structurile conceptuale de module sau obiecte și stabilesc datele
și prelucrările pentru fiecare din ele), programatori (realizează codificarea
programelor), ingineri electroniști, electricieni, alți tehnicieni (pentru
instalarea echipamentelor și, eventual, amenajarea spațiilor), ingineri de
sistem sau ingineri DevOps (pentru instalarea sistemelor de operare și

66
Algoritmică şi programare

integrarea aplicațiilor), precum și beneficiari sau utilizatori (pentru testarea


utilizării aplicațiilor și certificarea conformității cu cerințele). Documentele
elaborate în finalul acestei etape sunt: „Programe sursă” ale aplicației și
fișierelor de comandă, „Documentația aplicației” (care descrie structura de
module, funcționarea și configurarea aplicației), „Manualul de utilizare”,
„Fișe de testare” (care constată conformitatea utilizării cu cerințele). Darea
în exploatare a aplicației se face după o testare la utilizator (de obicei de 72
de ore), și dacă aceasta a decurs cu succes se încheie un „Proces verbal de
recepție”. Acest document încheie ciclul de realizare al aplicației; oricare
alte modificări solicitate și realizate după acest moment se consideră lucrări
separate, pentru care se parcurg din nou etapele (de la analiză până la
implementare și testare).
Exploatarea și dezvoltarea aplicației
După ce aplicația a fost testată și recepționată de către beneficiar ea
intră în exploatare, adică este utilizată pentru scopul pentru care a fost
realizată. „Darea în exploatare” este faza în care personalul utilizator
urmează cursuri de pregătire pentru utilizarea aplicației și, eventual,
conducerea asigură cadrul organizatoric (personal specializat, spații și
regulamente de lucru) pentru aplicația sau sistemul în cauză.
Pe durata exploatării aplicației (sau a sistemului de informatizare) pot
apare diverse probleme, care trebuie rezolvate spre a se asigura o
funcționare corectă. Dintre probleme amintim: actualizarea unor date ale
aplicației de tip parametric (de exemplu la modificarea legislației legată de o
aplicație de contabilitate), configurare periodică, administrarea resurselor
sistemului (imprimante, discuri, conturi utilizator), rezolvarea unor
incidente de operare sau ce apar după un accident (defect) sau după
modificări în echipamente. Acțiunile de rezolvare a unor asemenea
probleme se pot reuni în activitatea de întreținere a aplicației.
Persoanele implicate în această activitate sunt: ingineri DevOps,
ingineri de sistem (asigură configurarea corectă a sistemului de operare și a
programelor de aplicație), administratori de baze de date (asigură
configurarea și asistă utilizatorii în utilizarea corectă a bazelor de date)
administratori de rețea (asigură configurarea și menținerea bunei funcționări
a echipamentelor și programelor de comunicație), ingineri și/sau tehnicieni
de întreținere echipament, utilizatori obișnuiți și utilizatori „privilegiați” –
ultimii având de fapt sarcini speciale, de exemplu gestiunea resurselor
grupului de lucru, servicii de configurare specifică grupului de lucru;
calificativul de „privilegiat” se referă la drepturile (și răspunderile) extinse
pe care le au privind accesul la date și programe.
Exploatarea aplicației – în forma în care a fost achiziționată, are loc
până la apariția unei versiuni perfecționate (adică o dezvoltare - în engleză

67
Algoritmică şi programare

„upgrade”) sau până la inutilitatea ei (datorită apariției pe piață a unor noi


produse sau prin dispariția scopului aplicației). În măsura în care prin
modificarea aplicației se pot obține caracteristici mai performante, se poate
intra într-o etapă de dezvoltare a aplicației, în care se repetă toate etapele –
de la analiză la implementare, parcurse la realizarea aplicației. Evident, cea
mai marea parte a programelor din aplicația curentă nu ar trebui să sufere
modificări ci doar cele care nu mai sunt de actualitate sau necesită
perfecționări.
Limbaje de programare
În modulele anterioare s-au amintit unele limbaje de programare în
contextul declarării tipurilor de date dar și a tipurilor de comenzi necesare
descrierii prelucrărilor cu ajutorul sistemelor de calcul. De fapt, acestea sunt
cele două aspecte generice prin care omul comunică mașinii ce are de făcut:
cu ce (datele) și cum (comenzile). Un limbaj de programare apropiat omului
oferă independentă programatorilor fată de tipul mașinii de calcul,
permițând acestuia să se concentreze asupra rezolvării problemei, nu asupra
mașinii.
Un limbaj de programare este un set de cuvinte cu semnificații
precise, care se pot combina după reguli stricte pentru a exprima comenzi și
a descrie date necesare unui tip de prelucrare.
Limbajele de programare sunt limbaje artificiale, în care se scriu
programele pentru calculator. Limbajul trebuie să poată fi înțeles de către
om (care îl concepe), dar trebuie să poată fi executat de calculator. Din
această cauză, trebuie să respecte un formalism riguros. Din punctul de
vedere al comodității de utilizare de către om, există mai multe niveluri de
limbaje de programare: cod mașină, limbaje de asamblare, limbaje de nivel
superior.
În limbajul mașină, numit și cod mașină, instrucțiunile sunt scrise
binar (prin numere), așa cum sunt ele în memoria calculatorului în
momentul executării programului.
O instrucțiune mașină tipică are forma
<cod operație> <adresa operandului>, unde ambele părți ale
instrucțiunii sunt numere.
Codul mașină este executat direct de calculator, fiind foarte greu de
scris și înțeles de către om. Cu toate că s-au făcut diverse încercări de
elaborare a unui limbaj de programare universal, care să satisfacă cerințe
spre orice scop, diversitatea limbajelor de programare a crescut datorită pe
de o parte tendinței de specializare resimțită în lumea modernă, pe de altă
parte datorită unor interese comerciale ale firmelor producătoare de
software. Unele limbaje necesită cunoștințe profunde asupra structurii și
funcționării sistemului de calcul și sistemelor de operare, altele pot fi

68
Algoritmică şi programare

folosite de începători, unele limbaje se folosesc la programarea


microcontrolerelor (care comandă aparate moderne de jos nivel, în orice
domeniu – de la dispozitive în instalații industriale până la aparatură de uz
casnic și domestic), altele se folosesc la programarea super-calculatoarelor
(cu mii de procesoare funcționând în paralel utilizate în instituții cu regim
special, pentru calcul intensiv în mecanica fluidelor sau în controlul
traficului de zbor, etc.). Având o abordare pragmatică, un limbaj va fi
regăsit în diverse clase ce indică astfel caracteristicile sale.
Limbaje de programare uzuale
Anterior s-a făcut trimitere către anumite limbaje de programare C,
Pascal și Java pentru a exemplifica cele două aspecte importante: descrierea
datelor și structurilor de date precum și exprimarea comenzilor elementare
(instrucțiuni). Se prezintă mai jos, în fiecare paragraf, câte un limbaj de
programare, ordonate după gradul de extindere și frecvența de utilizare de
către programatori.
1. C (pronunțat ca în engleză „si”) este dezvoltat de Kernigan şi Ritchie
la Bell Laboratories în anul 1972, fiind ulterior limbajului denumit
B. C a fost dezvoltat pentru crearea de programe apropiate de mașină
(sisteme de operare, drivere) fiind legat de sistemul de operare
UNIX. Popularitatea sa cât și standardizarea de către ANSI l-au
impus ca limbajul cel mai larg acceptat de programatori. C este un
limbaj compilat, cu funcții pentru diferite prelucrări (intrare/ieșire,
matematice, etc.) conținute în fișiere biblioteci („library”), ce pot fi
accesate din program. Programele C sunt compuse dintr-una sau mai
multe funcții definite de programator, C fiind considerat un limbaj
structurat. Varianta C++ (dezvoltată de Bjarne Stroustrup) este un
limbaj de programare orientat pe obiecte, fiind extins cu directive
pentru crearea și manipularea obiectelor. Există alte diverse variante
îmbunătățite, ca Visual C++ (cu mecanisme de creare a interfețelor
grafice și lucrul în rețea), C# (pronunțat „si șarp”, cu servicii pe
Internet în categoria .net – „dot net”).
2. Java (pronunțat „giava”), dezvoltat de firma SUN Microsystems în
scopul declarat de a realiza aplicații pentru Internet prin compilator
și biblioteci gratuite, este similar limbajului C++ (orientat pe obiecte
și instrucțiuni identice) însă compilarea produce un „cod de octeți”
(nu cod executabil). Este extins pentru lucrul cu fire de execuție
(secțiuni de program ce pot rula independent), tratarea excepțiilor
(erori, sau întreruperi), securitate (execuția se face prin intermediul
„Java Virtual Machine” care interpretează și controlează „codul de
octeți” spre a nu permite acțiuni ostile – de ex. prin acces direct la

69
Algoritmică şi programare

resursele sistemului), portabilitate (poate rula pe orice calculator care


prezintă „Java Virtual Machine”).
3. JavaScript este un limbaj scriptural care adaugă paginilor web
facilități de animație și de interacțiune cu utilizatorul (diferit și
folosit mult mai des decât Java pentru programarea părții client
web). Este standardizat sub numele de ECMAscript.
4. HTML (Hyper Text Markup Language) este un limbaj scriptural de
descriere a documentelor, folosind marcaje ce specifică acțiuni de
formatare înscrise chiar în textul (conținutul) documentului. Paginile
Web sunt descrise prin acest limbaj, permit în plus legături cu alte
pagini distribuite spațial pe alte mașini (site-uri); astfel se poate
considera că textul documentului nu mai este înscris pe o foaie cu
două dimensiuni ci prezintă și adâncime – către alte texte (este hiper-
text). Informațiile din pagină, conținând text și imagini, sunt
descărcate („download”) și afișate pe mașina utilizatorului. În aceste
pagini, acțiunile de formatare privesc modul de scriere a textului
(litere îngroșate, cursive, etc.), structura documentului (denumiri de
modul, paragrafe, tabele) și interacțiunea cu utilizatorul (de exemplu
prin formulare ce permit transferul de date către site – „upload”).
5. XML (eXtensible Markup Language) este o extensie a limbajelor
din familia SGML, în care marcajele nu mai sunt predefinite și
stricte ci pot fi definite chiar de programator pentru a descrie diverse
tipuri de date dar și prelucrări. Este utilizat în principal pentru
partajarea informațiilor și textelor structurate în Internet.
6. SQL (Structured Query Language) este creat de firma IBM și
adoptat ca standard de ISO (International Standards Organization).
SQL (pronunțat „sicuăl”) este un limbaj de programare declarativ
pentru baze de date relaționale, oferind exprimări simple și intuitive
de interogare precum și de manipulare a tabelelor, formularelor și
rapoartelor în aplicații cu baze de date.
7. PERL (Practical Extraction and Report Language) este un limbaj
scriptural creat de Larry Wall ca o combinație între limbaj de
comandă (v. Bourne Shell) și C, pentru extragerea informației din
texte și generarea de rapoarte. Este folosit în special pentru generare
pagini HTML, pentru programe de lucru pe servere Web (programe
CGI), pentru animație sau interacțiune cu utilizatorul prin formulare
în pagini web.
8. PHP este un limbaj scriptural similar cu PERL, ce poate fi înglobat
în codul HTML al paginilor Web. Este folosit pentru aplicațiile pe
partea server web, pentru construirea paginilor web dintr-o bază de
date SQL (Oracle, MySQL). Prezintă una din cele mai mari

70
Algoritmică şi programare

biblioteci „open-source” (cod public și liber de a fi utilizat, modificat


sau distribuit).
9. Asamblare este limbajul mașinii într-o notație inteligibilă omului.
La compilarea unui program scris în limbaj de asamblare, conversia
se face direct în coduri binare, fiindcă instrucțiunile limbajului se
referă la acțiuni elementare ale procesorului (încărcare de regiștrii,
salturi condiționate, operații pe bit). Este utilizat pentru scrierea
programelor de control direct al perifericelor sau pentru operații
legate de echipament.
10. Bourne Shell este un limbaj scriptural care permite crearea de
fișiere de comenzi (în loturi - „batch”) pentru sistemul de operare
UNIX (alte limbaje similare sunt sh, bash, ksh, csh). În general,
orice sistem de operare prezintă limbajele de comandă (limbaje
shell), necesare lucrului imediat cu calculatorul, comenzile fiind
executate de un „interpretor de comenzi” furnizat cu sistemul de
operare.
Utilizarea unui limbaj de programare depinde de scopul și tipul
programării (indicate succint în inventarul de mai sus) dar și de obișnuința
sau preferințele programatorului.
Clasificări ale limbajelor de programare
O primă clasificare a limbajelor se poate face după paradigma de
programare – adică după ideea generală de soluționare a problemei.
Astfel, se deosebesc:
a. Programarea procedurală – privește datele și prelucrările ca entități
distincte (declarate separat) și folosește conceptele de modul pentru
prelucrare și orizont de vizibilitate pentru variabile. Un modul este
format din unul sau mai multe subprograme iar o variabilă (dată
declarată de anumit tip) este vizibilă (accesibilă) în cadrul unui modul
dar nu și din afara sa; modulele pot fi preluate din biblioteci de
subprograme predefinite (prefabricate de producătorul
compilatorului). Programarea procedurală este realizată prin limbajele
C, Pascal, Delphi, FORTRAN, COBOL.
b. Programarea imperativă – în care se furnizează calculatorului o listă
de instrucțiuni și un context de memorie (considerat drept starea
programului la un moment dat) care este modificat printr-o comandă
în alt context (altă stare, cu alte valori de variabile în memorie).
Funcționarea procesorului este de fapt imperativă, el urmărind pas cu
pas lista de instrucțiuni din program. Limbajele de programare uzuale
sunt imperative (FORTRAN, C, Perl), cele obiectuale (C++, Java)
adăugând doar noi facilități de lucru.

71
Algoritmică şi programare

c. Programarea declarativă – diferă de cea imperativă prin faptul că în


program se descrie pentru calculator un set de condiții, lăsând apoi
calculatorului sarcina să le satisfacă. În programarea declarativă, se
descriu relații între variabile în termeni de „reguli de inferență” (reguli
de obținere a unor noi valori din cele existente). Calculatorul
(înțelegând aici un program complex de tipul unui motor de inferență
sau unui mediu de baze de date ce rulează pe calculator) aplică un
algoritm fixat și interpretează relațiile spre a produce un rezultat.
Limbaje uzuale din categoria declarativă sunt Prolog și SQL.
d. Programarea funcțională – consideră prelucrarea drept evaluarea
funcțiilor matematice. Expresiile, în această paradigmă, sunt formate
din funcții ce combină valori iar execuția nu implică o secvență de
comenzi fiindcă programul definește CE și nu CUM se prelucrează
datele. De fapt limbajele de programare nu pot fi pur funcționale
pentru că rulează pe o mașină în paradigmă imperativă; se poate
aminti LISP ca limbaj funcțional, în care prelucrările sunt structurate
similar structurării datelor (de obicei în liste).
O clasificare a limbajelor de programare des întâlnită, consideră
nivelul limbajului, relativ apropiat (sau depărtat) de formularea limbajului
mașinii (mai precis al procesorului, ca dispozitiv electronic, binar).

Nivelul limbajului Caracteristici ale limbajului Exemple uzuale


jos Instrucțiunile sunt apropiate de limbajul Asamblare
mașinii, fiind translatate direct în instrucțiuni
mașină de către asamblor (un compilator
simplu)
mediu Instrucțiunile sunt transpuse în limbaj C, BASIC, Pascal,
de asamblare prin compilator, oferind în plus COBOL
biblioteci și servicii de configurare a resurselor
mașinii la execuție
înalt – compilat Instrucțiunile sunt transpuse într-un cod Java, Visual Basic,
intermediar folosind un interpretor, permițând Visual C
astfel controlul codului și portabilitatea sa pe
orice mașină
înalt - scriptural Instrucțiunile pe linii de program sunt Bourne Shell,
interpretate și executate fiecare în parte; liniile HTML, Perl,
se pot grupa în loturi și executate ca „fișier
(text) de comandă”
foarte înalt Structura limbajului este apropiată SQL
limbajului uman, descrie o metodă de
implementare; sunt limbaje declarative sau
pentru Inteligență Artificială
Tabel 4 Clasificarea limbajelor de programare după nivel

72
Algoritmică şi programare

În Tabel 4 se prezintă succint clasificarea după nivel a limbajelor, care


însă trebuie înțeles ca fiind din ce în ce mai apropiat de nivelul uman, deci
mai simplu pentru om, nu mai dificil.
O a treia clasificare se poate face după modul de declarare a tipurilor
de date, în limbaje cu:
• tipuri statice de date – tipurile declarate sunt stricte și
verificate de compilator (exemple C, C++, Java,
Pascal),
• tipuri dinamice de date – în care datele de tipuri
diferite pot fi interschimbate, interpretorul
nesemnalând eroare la o dată nouă cu tip nedeclarat
(exemple Lisp, JavaScript, Prolog).
Compilatoare și interpretoare
Programele în limbaj înalt sunt relativ ușor de înțeles de către om, dar
nu pot fi executate direct de către calculator. Există două modalități
principale în care un astfel de program poate fi pus în execuție: prin
compilare sau interpretare. Astfel, limbajele de programare permit
realizarea codului ce va fi executat după traducerea sa în limbajul mașină
prin:
• Compilare –traducerea are loc pentru întregul set de comenzi
(descrise ca un tot unitar, într-un „program”). Limbaje din
această categorie sunt C, C++, Java, Pascal, BASIC,
FORTRAN, COBOL.
• Interpretare – traducerea are loc linie cu linie (câte o
comandă la un moment dat), de la prima până la ultima din
setul dat. Limbaje din această categorie se numesc limbaje
scripturale, iar textul cu comenzile se numește script, „fișier
de comenzi” sau „lot de comenzi” (în engleză „batch”).
Exemple de limbaje script sunt HTML, Perl, PHP, limbaje de
comandă ale sistemului de operare („shell” – Bourne Shell,
bash, csh).
Compilarea este traducerea (translatarea) unui program în limbaj de
nivel înalt, numit program sursă, într-un program în cod mașină, numit
program obiect.

Compilatorul este un program care se execută pe calculator. El


primește ca date de intrare programul sursă și produce ca date de ieșire

73
Algoritmică şi programare

programul obiect, în cod mașină (binar). Acesta se stochează pe un suport


extern (pe disc) și poate fi ulterior executat.
Interpretorul este un program care execută direct programul sursă
scris în limbaj de nivel înalt. Executarea se face instrucțiune cu instrucțiune.
Se citește o instrucțiune din programul sursă, aceasta este translatată în cod
mașină care este transmis spre execuție procesorului, apoi se trece la
instrucțiunea următoare din programul sursă și se repetă ciclul. Dezavantaj:
execuția este mai lentă decât la programele compilate, deoarece se consumă
timp pentru interpretare.
Un avantaj important al programelor scrise în limbaj înalt este că ele
pot fi executate, cel puțin în principiu, pe orice tip de calculator. Întrucât,
codul mașină diferă de la un tip de calculator la altul, este necesar câte un
compilator sau un interpretor al limbajului respectiv pentru fiecare tip de
calculator pe care se intenționează să fie executat programul. Se spune că
programul sursă este portabil, dar nu și programul obiect.
Unele limbaje de programare, cum sunt Fortran, Cobol, Pascal, C,
C++ și altele, sunt compilate. Avantaj: executarea programului obiect se
face cu viteza mare, respectiv cu viteza de lucru a procesorului
calculatorului pe care se execută.
Alte limbaje, cum sunt Basic, Python, Pearl, Ruby și altele sunt
interpretate. Avantajul este că pot fi utilizate în regim interactiv: imediat ce
utilizatorul a introdus o instrucțiune de la consolă, ea poate fi executată de
către interpretor.
Există limbaje care pot fi executate în ambele moduri dar, de obicei,
au specific doar unul din ele. Limbajele script pot coexista pe aceeași
mașină; astfel, pentru selecția tipului de limbaj efectiv utilizat, prima linie
din lotul de comenzi script conține o directivă a sistemului de operare ce
indică în clar limbajul la care se referă fișierul de comenzi. Avantajul
compilatoarelor este acela că programul în cod executabil poate fi rulat
direct pe mașină, imediat după încărcarea lui în memorie de pe suportul
extern (disc).
Un caz special îl constituie limbajul Java, care este interpretat după ce
este „compilat” în așa-numitul „cod de octeți” numit bytecode, care este
apoi interpretat pe orice mașină ce prezintă mașina virtuală Java („Java
Virtual Machine”). Acest program este executat de către un interpretor, care
convertește instrucțiunile mașinii virtuale Java în instrucțiuni ale
procesorului calculatorului pe care se execută programul. Avantaj:
bytecode-ul poate fi executat pe orice calculator pe care există JVM
(portabilitate).

74
Algoritmică şi programare

Sintaxa si semantica limbajelor de programare


Fiecare limbaj de programare, ca orice alt limbaj, se caracterizează
prin anumite reguli de scriere corectă, numite reguli de sintaxă.
Orice instrucțiune a limbajului are o formă și o semnificație. Sintaxa
se referă numai la forma instrucțiunii, in timp ce semnificația este de
domeniul semanticii. Semantica se referă la modul in care trebuie
interpretată instrucțiunea respectivă. Este evident că, atunci când se concepe
un program, este necesar să se acorde atenție atât sintacticii, cât si
semanticii.
Dacă într-un program nu sunt respectate regulile de sintaxă,
compilatorul sau interpretorul sesizează anomaliile și le semnalează sub
forma unor mesaje de erori de sintaxă. În astfel de situații, codul de octeți al
programului respectiv nu va mai fi generat. Programatorul trebuie sa
remedieze erorile de sintaxă semnalate și să ceară repetarea compilării sau,
în cazul regimului interactiv, să reintroducă instrucțiunea pentru
interpretare. Acest procedeu se repetă, până când nu vor mai fi constatate
erori de sintaxă.
Insistăm asupra faptului că la compilare sau interpretare sunt
semnalate numai erorile de sintaxă. Dacă un program este corect sintactic,
dar este conceput greșit, va fi executat de către calculator, dar rezultatul
obținut nu va fi cel scontat. Corectitudinea conceperii programului este în
întregime responsabilitatea programatorului.
Ingineria programării
Termenul „inginerie” duce cu gândul imediat la tehnică și industrie.
Așa cum termenul „tehnologie” din acronimul TIC nu se referă la tehnologii
în industria metalurgică ci doar la suma de tehnici și mijloace din
informatică, termenul „inginerie a programării” se referă la tehnică drept
abordare sistematică, de producție eficientă a programelor pe calculator (în
mod industrial), nu la un domeniu ingineresc (metalurgie spre exemplu). De
fapt, etapele de realizare a programelor (prezentate) constituie esența acestei
abordări sistematice.
Ca și la alte produse, calitatea produselor program (a software-ului) nu
este doar un deziderat de piață ci și un scop impus de toleranțele în care
produsul trebuie să-și realizeze utilitățile. Astfel, în timp ce pentru o mașină
de spălat automată toleranța de 2% în alimentarea cu detergent este admisă
(și considerată foarte bună), un sistem de contabilitate care are erori de 2%
este inacceptabil. De aceea, sunt necesare și pentru software modalități de
măsurare (metrici) și evaluare a performanțelor și de aici a calității
produselor program.
Ingineria programării este abordarea sistematică, disciplinată și
cuantificată a dezvoltării, operării și întreținerii produselor program.

75
Algoritmică şi programare

Definiția de mai sus subliniază că această abordare este o aplicare a


ingineriei în programare (de aici și numele): aplicând tehnicile și practicile
de inginerie a programării se creează programe de bună calitate, cu mare
productivitate, cu întreținere și dezvoltare facile. Dezideratul ingineriei
programării este satisfacerea cerințelor clientului produsului software,
respectând restricții de calitate, cost și durată, prin:
i) utilizarea unei metodologii clare începând cu faza de analiză,
apoi cea de proiectare și de implementare a programelor;
ii) conducerea activităților, desfășurate pe parcursul proiectul,
conform unor principii de management stricte („project
management”);
iii) analiza și evaluarea performanțelor codului, a structurii de
module, funcționării și utilizării produsului;
iv) documentarea clară și completă a funcționării și utilizării
produsului software;
v) urmărirea produsului livrat la beneficiari și pe întreaga durată
a ciclului său de viață, pentru actualizarea cu versiunile noi și
îmbunătățite.

Factori de performanta ale produselor software sunt:


funcționalitatea (în ce măsură produsul îndeplinește funcțiile propuse),
ușurința în utilizare (simplitatea de învățare și operare), fiabilitatea
(funcționarea corectă și robustă – tolerantă la operare sau date greșite),
eficienta (privind resurse utilizate – memorie putina, viteză de execuție),
flexibilitate (ușurința de adaptare și modificare după cerințe), portabilitate
(posibilitatea de transfer pe alte mașini și sisteme de operare, respectarea
standardelor), întreținerea comodă (acces la cod sursă și compilare,
modificare ușoară)
Ingineria programării oferă metrici (adică sisteme de măsurare) de
evaluare pentru:
• produs – privind proiectarea, codul sursă și cazurile de test ale
programului;
• procesul de dezvoltare – privind activitățile de analiză, proiectare și
programare;
• persoanele implicate în proiect – privind eficienta individuală a
proiectanților, programatorilor și testărilor.

Pentru aplicații mari, organizarea proiectului implică un șef de proiect


(contribuie la proiectare și dezvoltare în proporție de 30%, distribuie sarcini
și coordonează coechipierii), adjunct (planifică și coordonează programarea
și testele, asigură calitatea produsului), secretar de proiect (execută sarcini

76
Algoritmică şi programare

administrative privind protocoale cu beneficiarul, biblioteci, gestiunea


termenelor și costurilor), programatori / dezvoltatori (specialiști în medii și
limbaje de programare sau în instrumente de dezvoltare software).
Analiza, proiectarea și implementarea respectă o metodologie
specifică (uzual din categoriile „structurată” sau „obiectuală”), iar testarea
se execută atât în condiții de laborator cât și pe cazuri reale. Întreținerea
aplicației este foarte importantă, așa cum reiese din proporția uzuală a
costurilor pentru software: Analiza – 10%, Proiectarea – 10%,
Implementarea – 10%, Testarea – 20%, Întreținerea – 50%.
O importantă deosebită o are documentația, care trebuie întocmită pe
parcursul proiectului la fiecare etapă. Documentația se adresează
dezvoltatorilor, utilizatorilor și personalului de întreținere. Documentația ce
trebuie întocmită a fost deja amintită la fazele ciclului de viață ale
programului. De remarcat că programele trebuie amplu documentate
(comentate) chiar în codul sursă, unde algoritmul trebuie explicat.
Tehnici și instrumente de realizare a programelor
Istoric, în evoluția proiectării aplicațiilor și a programării, au existat
următoarele abordări – din care au rezultat metodologii specifice:
i) Proiectarea/programarea nestructurată – în care aplicația nu
este gândită și realizată modular ci ca un tot, cu salturi
interne greu de controlat și urmărit. Dezvoltarea și
întreținerea programelor este extrem de ineficientă și greoaie.
Această abordare se poate compara cu generația
televizoarelor cu tuburi electronice („lămpi”) și componente
interconectate prin sârme (formând uneori „ghemuri”).
ii) Proiectarea/programarea structurată – în care aplicația este
divizată în module specializate pentru anumite operații,
asamblate apoi (mai precis apelate) de către aplicația
„principală”. Abordarea se poate compara cu generația
televizoarelor realizate pe module specializate (modul
alimentare, selectare canale, modul sunet, etc.) montate pe
placa de bază. Avantajele provin din faptul că pentru
depanarea sau modificarea unui modul se lucrează numai cu
acesta, nu cu întreg aparatul (respectiv întreaga aplicație). În
programare se elimină salturile necondiționate, deoarece,
chiar din etapa de proiectare, prelucrările sunt ierarhizate
astfel încât un modul să „apeleze” un alt modul specializat
pentru o acțiune anume.
La aceste abordări datele sunt analizate separat de prelucrări. În cazul
ii) modulele sunt realizate prin subprograme (funcții) iar datele sunt
declarate separat de prelucrări. Declararea datelor se face prin exprimări

77
Algoritmică şi programare

conform celor menționate anterior, acestea fiind considerate „date de


intrare” pentru funcțiile care le prelucrează. Proiectarea se bazează pe o
parcurgere „top-down” (de la mare la mic) a problemei, pornind de la
ansamblu și apoi trecând la părți, module; pentru fiecare din acestea se
discriminează datele și prelucrările corespunzătoare.
iii) Proiectarea/programarea orientată obiect – în care aplicația
se construiește din obiecte care încapsulează proprietăți și
metode – adică informații (date) și prelucrări (operații asupra
datelor). În acest mod, în etapele de analiză și proiectare se
concep clasele de obiecte similare celor din lumea reală a
problemei de rezolvat, cu „modul lor de utilizare”, iar
rezolvarea problemei se face prin manipularea obiectelor
create la implementare. Abordarea se poate compara cu
viziunea utilizatorului de televizoare, în care clasa de obiecte
„televizor” trebuie să dețină ecran și legătură prin cablu (ca
informații - date) și butoane de acționare pornit/oprit, reglaj
volum, comutare canale, etc. Evident, rămâne în sarcina
producătorului să realizeze linia de fabricație a clasei de
televizoare (acesta este programatorul) și apoi magazinului să
vândă utilizatorului un televizor anume (acesta este
declararea unui obiect a de tip „televizor”, similar cu
declararea unui tip de dată inițializată).
iv) Proiectarea/programarea cu componente – în care aplicația
se construiește prin componente gata fabricate. Abordarea
este similară construirii televizorului din circuite integrate
specializate, care doar se asamblează în modul dorit spre a
produce un televizor CRT, TFT sau LED, SMART sau
clasic, etc. Similar cu primirea componentelor fizice vin de la
fabrică, componentele software vin de la producători/
dezvoltatori de software și sunt utilizate de programatori
pentru a crea produsul dorit.
La ultimele două abordări, proiectarea decurge „bottom-up” (de la
mic la mare) adică se face inventarul de obiecte sau componente aflate la
dispoziție și apoi se „construiesc” aplicații prin asamblarea acestora. Datele
și programele nu mai au o delimitare evidentă: pentru programator datele
pot deveni programe și programele – date. Continuând similitudinea cu
producția de televizoare, generația iii) reprezintă crearea de televizoare
artizanale – fiecare producător realizează televizoare în tehnologie proprie,
pe când cu iv) producătorii folosesc componente standard pentru părți de
televizor, oferind înfățișare și performante speciale produselor proprii fată
de ale altor producători. Ultimele două generații permit și stimulează
industria software, fiindcă proiectantul și programatorul nu mai sunt

78
Algoritmică şi programare

implicați în atâtea amănunte de lucru la fiecare program în parte (amănunte


pe care nu le pot stăpâni perfect și nici nu au productivitate dacă sunt multe
sau necesită mulți coechipieri) ci se pot orienta pe producție, pe nevoile
clienților și/sau pe cererea pieței. Proiectantul și programatorul au „în spate”
o industrie de componente pe care trebuie doar să știe să le asambleze
pentru a face un produs la comandă.
v) Proiectarea/programarea cu agenți – în care aplicația (ca
agent) evoluează de sine stătător și comunică cu alte aplicații
(alți agenți) pentru a-și îndeplini misiunea. Spre exemplu,
există programe agent de căutare care pot „naviga” prin
Internet, se pot ”stabili” la anumite site-uri și comunica cu
alți agenți, scopul lor fiind acela de a găsi și transmite
informațiile pentru care au fost creați și lansați în spațiul
cibernetic.
În timp ce pentru primele patru generații aplicația se lansează și se
executa prin operarea programului direct de către om– adică inițiativa
aparține omului, generația v) introduce programe cu inițiativă, adică
programe care după lansare au existență și acțiuni independente de omul în
contul căruia execută prelucrările. Programul-agent se poate multiplica,
comunică cu alți agenți, precum și cu „baza”, formează grupuri și chiar
stabilește relații sociale și limbaje de comunicare între agenți; se poate
spune că din „obiecte” informatice aceste tipuri de date devin „ființe”
informatice.
La inventarul abordărilor prezentate mai sus pentru proiectarea
aplicațiilor, este necesar să se adauge încă două – care nu aduc metodologii
conceptual noi ci doar specifice unor instrumente frecvent folosite în
realizarea aplicațiilor:
vi) Proiectarea aplicațiilor cu Baze de Date – în care datele se
structurează în tabele, adică mulțimi de articole. De fapt, un
articol (ca linie din tabel) reprezintă un obiect iar tabelul
colecția de obiecte de același fel din problema de rezolvat –
adică tabelul reprezintă o entitate (o categorie conceptuală de
obiecte). Structura de entități cu relații între ele formează
modelul problemei de rezolvat, iar aplicația manipulează
datele din tabele cu comenzi specifice acestei reprezentări.
Metodologiile de proiectare a aplicațiilor cu baze de date
urmează etapei de analiză, iar proiectarea datelor și
prelucrărilor se realizează separat – datele ca tabele iar
prelucrările ca operații cu acestea, vizând direct
instrumentele software cu care se vor realiza aplicațiile.
vii) Proiectarea aplicațiilor Web (servicii Internet) – în care
datele sunt, de obicei, pagini cu informații ce trebuie

79
Algoritmică şi programare

vizionate de utilizatori prin Internet sau datele provin de la


utilizatori prin formulare completate de către aceștia, iar
prelucrările sunt operații de navigare, afișare și actualizare a
datelor (stocate adesea în baze de date). Proiectarea
aplicațiilor Web se bazează pe arhitectura Client-Server, pe
principii de marketing, impact estetic și emoțional, precum și
pe utilizarea sistematică a instrumentelor de proiectare și
editare de pagini web (cu imagini, formulare, animații și
hiper-legături)
Ultimele două abordări de proiectare sunt preferate de către multi
producători de produse software (firme, profesioniști în informatică sau
chiar practicieni în informatică proveniți din alte domenii) pentru că cele
mai multe aplicații privesc două activități umane foarte frecvente:
• gestiunea de resurse – fie ele bunuri de consum sau industriale,
resurse umane sau financiare, mijloace de transport sau documente,
care se pot stoca și manipula folosind baze de date;
• comunicarea informațiilor și interacțiunea prin Internet între
persoane, între firme și persoane (B2C – „Business to Client”) sau
între firme (B2B – „Business to Business”) – pentru activități
economice, sociale, administrative sau educaționale.

Din aceste abordări au rezultat tehnici și instrumente specifice de


programare și de realizare a aplicațiilor, cu influențe în toate etapele ciclului
de viață ale unui program.
Caracteristici ale programării orientate obiect
Fiindcă cea mai mare parte a aplicațiilor actuale folosesc conceptul de
obiect și sunt realizate obiectual, se vor prezenta în continuare caracteristici
ale programării obiectuale, pentru familiarizarea cu termenii și abordările
întâlnite în submodulele următoare.
Programarea orientată obiect (POO) se bazează pe clase, ca
abstractizări ce reunesc date și prelucrări posibile asupra lor. Un obiect
realizat (instanţiat) într-o clasă dată, prezintă anumite valori pentru date
(identificate ca proprietăți ale obiectului) și o anume comportare
(identificată prin metode de modificare a proprietăților).
POO vizează, în principal, următoarele aspecte:
• Crearea și manipularea de obiecte – prin care se modularizează
acțiunile programului încă din faza de analiză, atunci când se
identifică obiectele în problema reală;
• Refolosirea codului – prin care obiecte odată codificate se pot
reutiliza ori de câte ori este necesar, fiind grupate în colecții
(denumite biblioteci sau pachete).

80
Algoritmică şi programare

Aceste deziderate se obțin ca urmare a caracteristicilor programării


obiectuale, cele mai importante fiind:
a. Abstractizarea – prin care un obiect devine modelul unui
„actor” ce prezintă o stare (și o poate modifica), execută
acțiuni sau comunică cu alte obiecte din sistem.
b. Încapsularea – prin care accesul la proprietățile obiectului se
poate face numai prin metodele definite. Obiectul prezintă o
interfață către alte obiecte, prin care se specifică modalitățile
sale de manipulare.
c. Moștenirea – prin care o clasă de obiecte poate fi baza altor
clase (denumite clase derivate), proprietăți și metode esențiale
ale primei fiind preluate în întregime de celelalte. Se
realizează astfel specializarea claselor (și obiectelor).
d. Polimorfismul – prin care o metodă a unui obiect din clasă
derivată produce o comportare diferită față de cea a clasei de
bază.
Tipuri și structuri de aplicații
Structura unui program a fost prezentată, indicând părțile specifice ale
programului principal, pentru un limbaj de programare comun (procedural și
cu tipuri statice de date) cum sunt C, Pascal sau Java.
În sine, o aplicație cuprinde un program principal care are rol de
„dispecer” pentru prelucrările efective ale aplicației. Lansarea aplicației se
realizează la inițiativa utilizatorului, care înscrie o comandă (în forma text –
linie comandă) sau accesează o pictogramă într-o interfață grafică. Execuția
aplicației începe, în general, cu prezentarea unei interfețe de interacțiune cu
omul (printr-un interpretor de comenzi sau meniu). De obicei, o aplicație
este un program compilat și stocat în forma executabilă, fiind lansat (adică
încărcat în memoria de lucru și executat pe întreg lotul de instrucțiuni) la
inițiativa utilizatorului.
Structura generică a unei aplicații cuprinde două părți generice:
colecțiile de date („data” - adică valori cu care se lucrează) și logica de
prelucrare („business logic” - adică acțiunile asupra datelor). De exemplu, o
aplicație bancară conține o parte privitoare la conturi și valorile lor, o parte
privitoare la operațiuni de transfer între conturi.
Aplicații de Baze de Date
O categorie specială de aplicații sunt create și funcționează prin
intermediul Sistemelor de Gestiune a Bazelor de Date. Aceste aplicații
organizează datele în structuri de tip articol care apoi sunt grupate în
„tabele” ce pot fi stocate ca fișiere (pe suport extern – disc). Pentru
prelucrarea datelor, se folosesc programe scrise în limbaje proprii SGBD

81
Algoritmică şi programare

sau în limbajul standard SQL. Conceptual, o aplicație cu baze de date


manipulează „obiecte” de tipurile:
• Tabel – structura pe linii (articole) și coloane (câmpuri ale
articolelor) ce stochează datele, cu mijloace de modificare și control
a valorilor acestora;
• Interogare – exprimarea cererii de extragere, după condiții impuse,
a unui set de date din unul sau mai multe tabele, folosind un limbaj
(de ex. SQL) sau o grilă (un tabel generic prin care cererea este
ilustrată cu un exemplu);
• Raport – document text generat automat, conținând date sintetice și
analitice obținute din tabele sau interogări, prezentate în mod
profesionist (cu antet, subsol, etc.);
• Formular – machetă de încărcare și consultare a datelor, pentru
interacțiunea comodă cu utilizatorul (în mod grafic, prin câmpuri și
indicații pe loc);
• Modul – secțiune de program (scris în limbajul de manipulare a
datelor) care realizează prelucrări complexe, eventual combinații de
interogări, formulare și rapoarte generate conform logicii de
prelucrare a aplicației

Programele sunt interpretate sau compilate în cod intermediar, astfel


că execuția lor nu poate avea loc decât în prezenta mediului de baze de date
(SGBD) care este rezident în memorie și coordonează toate acțiunile din
program.
O bază de date este constituită din mai multe tabele, cu legături între
ele, pe lângă care se prevăd module de prelucrare specifice aplicației vizate.
Cele mai uzuale aplicații cu baze de date privesc gestiunea resurselor într-un
domeniu dat: contabil, financiar-bancar, mijloace fixe, stocuri (magazii sau
magazine), sisteme de vânzări, resurse umane. Multe aplicații în Internet au
în fundal un server de baze de date (mașinile de căutare, magazine virtuale,
etc.).
Aplicații client-server
Există aplicații în rețele de calculatoare care trebuie să asigure
transferul de date și prelucrarea acestora la distanță. Datele sunt stocate la
un punct central, pe un calculator care le gestionează printr-un program
denumit server pentru că oferă servicii (de acces și prelucrare date) la
distanță, rulând în permanență și așteptând cereri de la utilizatori. Pe mașina
locală a fiecărui utilizator, există un program denumit client prin intermediul
căruia utilizatorul poate solicita servicii serverului distant; datele „se
descarcă” („download”) de la server pe mașina utilizatorului, unde sunt
prelucrate local și afișate (prin interfața utilizator) de către programul client.

82
Algoritmică şi programare

Structura de aplicație cu două entități este denumită, în jargonul informatic,


2-tier.

Fig. 13 Arhitectura Client-Server 2-tier (cu două părți)


Primele aplicații în rețea realizau toate prelucrările pe o mașină
centrală (numită gazdă) iar mașina locală era folosită doar pentru afișare
text și preluarea datelor de la utilizator (terminal), adică aveau „o parte” 1-
tier. Avantajele modului de lucru 2-tier provin din faptul că datele pot fi
gestionate și asigurate mai bine într-un singur punct (nu distribuite în mai
multe puncte) dar prelucrările nu încarcă doar mașina centrală ci și mașinile
locale (ele fiind mai multe și încărcate temporar). În această structură, partea
client conține programele de prelucrare locală și de prezentare a rezultatelor
către utilizator, complementar părții server – care asigură prelucrări de acces
și transfer a datelor centralizate. Cele două piese software, server și client,
conlucrează și comunică prin intermediul unui protocol (ca un limbaj cu set
de reguli pentru formularea și servirea cererilor), iar comunicația fizică se
realizează prin infrastructura de comunicație (rețea de calculatoare).
Un exemplu uzual de aplicație client-server este WWW, în care partea
server găzduiește paginile web (și programele de interacțiune cu utilizatorul)
iar partea client o constituie navigatorul Internet („browser” ca MS Internet
Explorer, Mozilla Firefox, Safari sau Google Chrome). Protocolul de
comunicație între cele două piese software server și client este HTTP
(HyperText Transfer Protocol) sau HTTPS (HyperText Transfer Protocol
Secure) și apare indicat chiar în adresa de acces (URL) a serverului.
Varianta în care partea server conține pe aceeași mașină si logica de
prelucrare și datele este ilustrată în figura de mai sus și are două părți
(denumită de aceea arhitectură 2-tier). Un dezavantaj al acestei arhitecturi
este siguranța precară a datelor în cazul când un utilizator rău intenționat,
având acces la mașina server, poate depăși mijloacele de verificare efectuate
de logica de prelucrare centrală (programul server) și are acces direct la
datele stocate pe această mașină.
Pentru creșterea siguranței accesului la date, este indicat ca partea
server să fie separată în alte două părți: una care să conțină logica de
prelucrare (SERVER-P) și este legată direct cu clienții, alta să conțină datele
(SERVER-D) și la care are acces doar logica de prelucrare centrală – așa

83
Algoritmică şi programare

cum indică figura de mai jos. În acest fel, după accesul utilizatorului la
mașina SERVER-P, logica de prelucrare centrală verifică autenticitatea și
drepturile utilizatorului, apoi – doar prin intermediul programului server,
oferă acces la datele aflate pe una sau mai multe servere cu date (SERVER-
D 1 , ... SERVER-D n-2 ).
Arhitecturile, în acest caz, sunt de tip 3-tier (dacă există doar un
singur server SERVER-D 1 ), respectiv n-tier (dacă există mai multe servere
SERVER-D 1 ... SERVER-D n-2 ). Aplicații de baze de date pot funcționa în
arhitectura client-server 2-tier. În forma 3-tier funcționează aplicații
distribuite – cum ar fi de exemplu o aplicație de vânzări de acțiuni, în care
datele despre cursul acțiunilor, apoi știrile despre acționari sau companii,
respectiv tranzacțiile se găsesc pe mașini diferite.

Fig. 14 Arhitectura Client-Server 3-tier și n-tier


Aplicațiile în arhitectura client-server sunt cele mai răspândite în
Internet astăzi, prezentând și forme în care cele două părți sunt simetrice:
serverul poate fi client și reciproc, în scopul comunicării bidirecționale de
date stocate pe cele două (sau mai multe) mașini (v. peer-to-peer).
Miniaplicaţii
O categorie specială de program o constituia miniaplicația („applet”)
– ca program ce nu există de sine stătător ci doar în cadrul unei (alte)
aplicații – de exemplu o aplicație web cu pagini conținând text și imagini.
Miniaplicația „applet” poate rula doar când este lansată de un eveniment
extern – click pe o imagine, producând spre exemplu animația unui obiect
pe ecran. Miniaplicația „applet”, ca program, este descărcată de pe mașina
server pe mașina client și rulează (de obicei în mod interpretat) pe clientul
web (navigatorul Internet). Miniaplicația „applet” este un program script
(adică scris într-un limbaj scriptural) și interpretat linie cu line din textul

84
Algoritmică şi programare

sursă, acțiunile sale fiind astfel permanent controlate; din motive de


securitate. Miniaplicația „applet” nu are acces la sistemul de fișiere al
mașinii client iar comunicația o poate realiza doar cu mașina server de pe
care provine.
Complementar miniaplicațiilor „applet” există „servlet”, ce rulează pe
mașina server spre a asigura: acces securizat la baze de date, facilități pe
mașini de căutare în Internet, generarea de pagini web dinamice. Și acest tip
de program se scrie în limbaj scriptural și rulează interpretat, în cadrul unei
aplicații gazdă (de exemplu serverul web).
Instrumente software de dezvoltare a aplicațiilor
În accepția comună, clasică, programarea constă în scrierea textului
sursă într-un limbaj de programare ales, prin care se descrie un algoritm de
prelucrarea a datelor. Această activitate este foarte laborioasă și necesită
dese reveniri în scrierea textului (pentru corectura sintactică, pentru
optimizare sau chiar refacerea codului). Ingineria programării indică metode
și pași sistematici prin care se reduc la minim erorile în soluția problemei și
în realizarea codului. Totuși, pentru a obține o productivitate rezonabilă în
munca de programare sunt necesare instrumente software de asistare a
programatorului. Cele mai importante categorii de instrumente sunt
prezentate în cele ce urmează.
Interfețe, biblioteci și pachete de programare
Limbajele de programare actuale tind să fie cât mai concise – privind
cuvintele cheie și comenzile de prelucrare sau control de flux (atribuire
respectiv repetiție și decizie). O mare parte din prelucrări sunt implementate
ca subprograme sau pachete de funcții și formează biblioteci.
Programatorul trebuie să cunoască aceste biblioteci, cu inventarul lor și
modul de utilizare a subprogramelor aferente, pe care le apelează în
programul sursă; la faza de editare a legăturilor („linking”) ele se includ în
codul obiect al programului apelant într-una din modalitățile:
• Legătură statică – codul din bibliotecă este inclus în program la
compilare și formează codul executabil, care poate fi apoi rulat
independent de bibliotecă;
• Legătură dinamică – codul din bibliotecă face parte din sistemul de
operare și este inclus în codul executabil la încărcarea sa în memorie
(„loadtime” - adică la lansarea programului) sau chiar în timpul
execuției („runtime”).

Legarea statică a subprogramelor din biblioteci este folosită curent de


către mediile de programare C/C++ și Java, în vederea realizării
programului executabil. Fișierele bibliotecă conțin module precompilate și
sunt parte a compilatorului sau mediului de programare a limbajului,

85
Algoritmică şi programare

furnizat de producător. Bibliotecile sunt denumite la limbajul C „headers”,


sunt specializate pe tipuri de date și prelucrări (standard, pentru șiruri de
caractere, pentru funcții matematice, etc.) și se includ în programul obiect
dacă au fost apelate cu directivă preprocesor #include <…> la începutul
programului sursă. În limbajul Java, subprogramele sunt de fapt clase
predefinite, grupate în pachete (package) cu o structură arborescentă și
incluse în programul sursă prin directiva import.
Un tip de bibliotecă foarte utilizat este Interfața de Programare a
Aplicațiilor (API), care conține seturi de funcții, proceduri, variabile și
structuri de date ce pot fi folosite de către programator în aplicații ca obiecte
precompilate, apelate standard. Există API care fac parte din sistemul de
operare sau livrate ca pachet independent, API poate fi proprietară (exemplu
cea pentru PlayStation a firmei Sony) sau poate fi distribuită gratuit (cum
este cea de la Microsoft Windows).
Sistemele de operare precum Windows și Linux folosesc legătura
dinamică la încărcarea programului executabil („loadtime”). Biblioteca de
legătură dinamică este uzual denumită prin acronimul DLL (Dinamic Link
Library) la Windows și bibliotecă partajată („shared library”, ELF) la
sisteme UNIX.
Biblioteca conține module executabile pre-compilate și este stocată pe
disc ca fișier separat. Atunci când mai multe aplicații utilizează aceeași
bibliotecă dinamică, sistemul de operare o poate încărca pentru fiecare în
parte. Un mare dezavantaj al legăturii dinamice este faptul că programele
executabile depind de existența fișierelor bibliotecă pe sistemul pe care
rulează, execuția aplicației întrerupându-se când aceste fișiere lipsesc, sunt
redenumite sau au altă versiune.
La unele sisteme de operare legătura dinamică are loc pe durata
execuției programului („runtime”), astfel: programul executabil apelează o
Interfață de Programare a Aplicațiilor (API „Application Programming
Interface”) furnizând numele fișierului bibliotecă, un număr de funcție și
parametrii acesteia; sistemul de operare rezolvă importul de fișier (îl încarcă
în memorie) și face apelul funcției solicitate, în contul aplicației. Datorită
operațiilor suplimentare executate la fiecare apel de funcție, se poate ca
execuția programului să fie foarte lentă, deci să afecteze performanța
aplicației. De aceea legătura dinamică la momentul execuției nu este foarte
utilizată în sistemele de operare actuale.
Există diverse alte tipuri de biblioteci, cum sunt:
• Biblioteci pentru testare – care includ date de test dezvoltate specific
pentru un tip de aplicație anume, cu care se poate verifica
comportarea aplicației în situații diverse – înainte de a fi dată în
exploatare.

86
Algoritmică şi programare

• Biblioteci grafice – care conțin diferite obiecte grafice gata de


utilizat la proiecte din diferite domenii.
• Biblioteci de programe pentru diferite limbaje (Perl, Java) și diferite
utilizări (animații în pagini web, clase și prelucrări uzuale).
Trebuie făcută o deosebire clară între „pachete de programe” (sau
clase) – ca biblioteci și „pachete software” sau „pachete de aplicații” – ca
modalități de distribuție și instalare software achiziționat de la producători,
de exemplu pachete pentru aplicații de birou – MS Office al firmei
Microsoft, sau Apache OpenOffice care provine din StarOffice al firmei
SUN.
Dezvoltarea unui program scris într-un limbaj de programare ales,
decurge mai ușor dacă toate instrumentele necesare sunt integrate într-unul
singur, adică: editorul de text sursă, compilatorul, editorul de legături și
chiar depanatorul. Un Mediu de Dezvoltare Integrat (IDE – „Integrated
Development Environment”) este un instrument software care oferă aceste
facilități, fiind utilizat atât la proiectarea cât și la implementarea aplicației.
Întrucât interfața grafică utilizator (GUI – „Graphical User Interface”) este
partea de program care necesită cel mai mare efort de programare, acestor
medii li s-a adăugat modalitatea de proiectare și implementare vizuală a
interfeței grafice. Prin programarea vizuală, se plasează obiecte (de tipul
fereastră, buton grafic, casetă de text, etc.) pe o suprafață de lucru – care la
final va deveni suprafața de lucru a utilizatorului, în fundal, mediul de
programare producând cod prin care rezolvă partea de program ce va
construi obiectele interfeței.

87
Algoritmică şi programare

Mediul de programare RAPTOR


RAPTOR (Rapid Algorithmic Prototyping Tool for Ordered
Reasoning) este un instrument rapid de prototipizare algoritmică pentru
raționament ordonat, dezvoltat de către Martin C. Carlisle, Terry Wilson,
Jeff Humphries and Jason Moore, destinat în special pentru programarea
vizuală a algoritmilor. Astfel, se pot implementa diverși algoritmi utilizând
simbolurile grafice de reprezentare puse la dispoziție de RAPTOR, ca
ulterior, mediul de programare vizual să genereze codul algoritmului
implementat în diferite limbaje de programare, precum C/C++, Java, etc. Un
program dezvoltat în RAPTOR constă așadar dintr-o multitudine de
simboluri interconectate prin săgeți unidirecționale, simbolurile
reprezentând acțiuni ce trebuie executate iar săgețile stabilind ordinea în
care sunt interpretate simbolurile.
Interfața grafică utilizator
Interacțiunea om-mașină are atât aspecte tehnice cât și psihologice.
Aceste aspecte trebuie luate în considerare pentru a asigura lucrul eficient in
cadrul utilizării aplicațiilor pe calculator.
Interfața cu utilizatorul reprezintă totalitatea mijloacelor prin care
omul interacționează cu mașina, uzual reprezentate de perifericele de intrare
– ecran tactil, tastatură și indicator pe ecran (mouse, TrackBall, Touchpad) -
cu perifericul de ieșire - ecran și imaginile/rezultatele afișate/proiectate.

Fig. 15 Interfața grafica utilizator RAPTOR

88
Algoritmică şi programare

După cum bine știm, orice program se deschide într-o fereastră. O


fereastră aplicație este o reprezentare standard a instrumentelor uzuale de
lucru cu aplicația. Aceste instrumente sunt reprezentate grafic în diverse
moduri și permit acționarea lor cu ajutorul mouse-ului sau al tastaturii.
Elementele generice ale mediului de programare vizual RAPTOR,
precum și rolul fiecăreia sunt:
• Bara de titlu 1 prezintă numele aplicației, precum și numele
documentului;
• Bara de meniu 2 cuprinde categorii (opțiuni) accesibile în
aplicația dată, din care trei sunt regăsite la orice aplicație: File
(manipularea datelor specifice aplicației), Edit (copiere, căutare,
ștergere date) și Help (ajutor imediat - structurat pe conținut,
index și căutare);
• Bara de unelte 3 cuprinde comenzi uzuale ilustrate prin
desene intuitive, acționate ca butoane grafice. Comenzi de
editare, lucru cu fișiere și cu ferestre au o replică în bara de
unelte. Aici găsim totodată, comenzile pentru executarea
programului, stabilirea vitezei cu care fie evaluate simbolurile
precum și modalitatea de mărire/micșorare a algoritmului din
zona de lucru (Zoom);
• Panoul de simboluri 4 aflat în partea stânga, imediat sub bara
de unelte, conține 6 tipuri de simboluri, fiecare reprezentând un
tip unic de instrucțiune. Acestea sunt: Assignment (instrucțiunea
de atribuire), Call (apelul de procedura), input , output, Selection
(instrucțiunea decizională) și Loop (instrucțiunea repetitivă).
• Watch window 5 reprezintă fereastra de evenimente în
evaluarea simbolurilor, unde sunt afișate variabilele si valorile
lor in timpul rulării programului/algoritmului.
• Work space 6 reprezintă zona de lucru unde, cu ajutorul
simbolurilor, dezvoltam vizual algoritmul.
• Consola Master 7 , reprezintă interfața grafică unde sunt
afișate rezultatele (ieșirile din program) precum și numărul de
simboluri evaluate.
Structura unui program
Conform descrieri anterioare, programul constă dintr-o multitudine de
simboluri interconectate prin săgeți unidirecționale, unde:
• săgețile determină ordinea în care sunt interpretate simbolurile;
• simbolurile reprezentă acțiuni ce trebuie executate;

89
Algoritmică şi programare

• lansarea în execuție a unui program începe cu evaluarea simbolului


START;
• execuția programului constă în evaluarea simbolurilor, urmând calea
indicată prin săgeți, pornind de la START până la END, inclusiv;
• execuția unui program se termină atunci când se ajunge la simbolul
END.

Fig. 16 Calculul ariei cercului in RAPTOR


Astfel, fiecare algoritm dezvoltat în RAPTOR poate fi vizualizat și
urmărit pe tot parcursul executării lui, iar ulterior, convertit în alt limbaj de
programare (Ada, C#, C++, etc.) utilizând opțiunea Generate.
În dezvoltarea unui algoritm trebuie sa parcurgem trei etape: input,
prelucrare/procesare și output. Trebuie specificată clar diferența dintre
programator și utilizator în conceperea, implicit utilizarea unui produs
program. De aceea, ținând cont de faptul că la execuția programului,
utilizatorul introduce doar datele de intrare pentru a vizualiza rezultatele
dorite, programatorul trebuie să dezvolte programul de așa manieră încât să-
i afișeze utilizatorului într-un mod clar si neinterpretabil ce operații trebuie
să efectueze, pe tot parcursul execuției programului pentru a preîntâmpina
eventualele probleme de percepție/înțelegere ale utilizatorului.
Totodată, pentru exemplificare, vom dezvolta algoritmul de calcul
pentru aria cercului, ținând cont de etapele descrise mai sus.

90
Algoritmică şi programare

Operații de intrare(input)
Pentru problema propusă, avem nevoie de raza cercului, implicit o
singură dată de intrare. În informatică exista cel puțin 3 posibilități de a
efectua aceeași operație, astfel, pentru inserarea simbolurilor avem
următoarele modalități:
o drag and drop
o selectarea simbolului din Panoul de simboluri 4 , apoi
click în Work space 6 , pe ramura unde se dorește
inserat simbolul.

o în Work space 6 , dreapta click pe ramura unde se


dorește inserat simbolul. Din lista derulantă se alege
opțiunea dorită.

Fig. 17 Inserarea simbolurilor

Orice simbol inserat în Work space 6 , trebuie editat de așa manieră


încât să respecte întocmai algoritmul de rezolvare a problemei, în cazul
nostru, calculul ariei cercului.

91
Algoritmică şi programare

Fig. 18 Editare simbol input


În cazul nostru, input-ul este raza cercului. Editarea oricărui simbol se
face prin două modalități, dublu click pe simbol sau dreapta click pe simbol
si alegerea opțiunii de editare (Edit).
Operația de input presupune 2 procese, enunțarea cererii către
utilizator, formulată cat mai clar și concis, în zona PROMPT, respectiv
declararea variabilei (în câmpul de text variable) care va stoca ceea ce se
preia de la tastatură (ce tastează utilizatorul). În cazul nostru îi vom cere
utilizatorului să introducă o valoare pentru raza cercului (Enter radius) pe
care o salvăm în variabila radius.
Operații de prelucrare procesare
Reprezintă operațiile efectuate cu simbolurile Assignment (atribuire),
Call (apel procedura/subschemă logică), Selection (decizia) și Loop
(repetiția). Aceste operații vor fi detaliate pe larg în secțiunile următoare.
Pentru exemplul enunțat anterior, calculam aria cercului, funcție de
input, cu ajutorul simbolului de atribuire.

Fig. 19 Editare simbol de atribuire

92
Algoritmică şi programare

Astfel, atribuim variabilei area (câmpul de text SET) valoarea pe care


o va avea expresia pi*radius^2 sau pi*radius*radius. Pe măsură ce datele
sunt completate, se observă faptul că în zona Auto-complete apar sugestii
privind variabilele sau funcțiile existente.
Operații de ieșire (output).
Afișarea rezultatelor este o operație destul de laborioasă deoarece
implică operații de concatenare ale șirurilor de caractere precum și
transformări ale tipurilor de dată.

Fig. 20 Editare simbol output


Funcție de versiunea utilizată, există 2 opțiuni pentru output: text și
expression. Opțiunea text afișează șirul de caractere exact cum este tastat în
aria de text. Opțiunea expression implică, după cum am enunțat, operații de
concatenare pentru șiruri de caractere și variabile utilizate in elaborarea
algoritmului de rezolvare a problemei. În exemplul nostru, pentru a afișa
aria cercului, trebuie sa concatenam șirul de caractere The area is cu
valoarea variabilei area. Pentru concatenare utilizam semnul matematic +
aferent operației de adunare. Suplimentar, ținând cont de faptul că sirurile de
caractere se scriu intre ghilimele, vom insera in aria de text, aferenta
simbolului de output, următoarele: ”The area is ” + area.
Execuția algoritmului/programului
Execuția programului se poate face gradual (pas cu pas, prin evaluarea
unui singur simbol în cadrul unui pas) sau integral, pornind de la START
până la END.

93
Algoritmică şi programare

Fig. 21 Execuția programului/algoritmului


În fiecare moment al execuției programului știm cu exactitate ce
simbol este evaluat, lucru indicat de starea simbolului, respectiv culoarea
verde deschis. Totodată, în Watch window 5 (fereastra de evenimente în
evaluarea simbolurilor) devin vizibile variabilele disponibile.
Indiscutabil, algoritmul nostru de calcul a ariei unui cerc poate fi
îmbunătățit, totul depinde de performanța si rafinarea dorită. În figura de
mai jos, putem observa aceeași problema, rezolvată cu algoritmi diferiți.

Fig. 22 Algoritmi pentru calculul ariei cercului


În capitolul ce urmează vom elabora diverși algoritmi pentru diferite
probleme utilizând mediul de programare vizual RAPTOR.

94
Algoritmică şi programare

Scheme logice. Aplicații practice utilizând RAPTOR


Algoritm pentru calculul Sumei a două numere
Date de intrare: a, b – numere reale;
Condiții: cu/fără restricții ( a, b !=0);
Date de ieșire: Suma;

Fig. 23 Suna a două numere

Algoritm pentru calculul ariei si perimetrului unui dreptunghi


Date de intrare: b, i – numere reale;
Condiții: cu/fără restricții ( b,i !=0);
Date de ieșire: A (aria), P (perimetrul) – numere reale;

95
Algoritmică şi programare

Fig. 24 Aria și perimetrul dreptunghiului


Performanta (optimizarea) algoritmului de calcul

Fig. 25 Aria și perimetrul dreptunghiului - algoritmi optimizați

96
Algoritmică şi programare

Algoritm pentru rezolvarea ecuației de gradul I: a*x+b=0, unde a și


b sunt numere reale inserate (citite) de la tastatură
Date de intrare: a, b – numere reale;
Condiții: a, b !=0;
Date de ieșire: A (aria), P (perimetrul) – numere reale;

Fig. 26 Algoritm pentru rezolvarea ecuației de gradul I

Algoritm pentru rezolvarea ecuației de gradul 2: ax2+bx+c=0


Din enunțul problemei extragem următoarele cerințe:
o date de intrare: a, b, c – numere reale
o Condiții: a!=0 si d>=0
o date de ieșire: x1, x2 – numere reale
o x1 = (-b - sqrt(d)) / (2*a)
o x2 = (- b + sqrt(d)) / (2*a)
 sqrt reprezintă funcția radical
 d = b * b – 4 * a * c; d reprezintă delta
Pseudocod (rezolvarea ecuației ax2+bx+c=0)
o Start
o citește a, b, c

97
Algoritmică şi programare

o dacă a=0 atunci


o scrie "Ecuația nu este de gradul 2"
o sfârșit
o altfel
o d=b*b–4*a*c
o dacă d<0 atunci
o scrie "Ecuația are soluții complexe"
o sfârșit
o altfel
o x1 = (-b - sqrt(d)) / (2 * a)
o x2 = (-b + sqrt(d)) / (2 * a)
o scrie x1, x2
o sfârșit

Schema logică 1 (rezolvarea ecuației ax2+bx+c=0)

Fig. 27 Schema logica 1 pentru rezolvarea ecuației ax2+bx+c=0)

98
Algoritmică şi programare

Schema logică 2 (rezolvarea ecuației ax2+bx+c=0)

Fig. 28 Schema logica 2 pentru rezolvarea ecuației ax2+bx+c=0)

Algoritm pentru Suma Gauss (suma primelor n numere naturale)

Fig. 29 Algoritm pentru Suma Gauss

99
Algoritmică şi programare

Algoritm pentru Suma pătratelor/cuburilor primelor n numere


naturale

Fig. 30 Suma pătratelor/cuburilor primelor n numere naturale

Algoritm pentru calculul ∑𝒏𝒌=𝟏 𝒌(𝒌 + 𝟏)

Fig. 31 Calculul sumei k(k+1)

100
Algoritmică şi programare

𝒌
Algoritm pentru calculul ∑𝒏𝒌=𝟏 �𝒌+𝟏�

Fig. 32 Calculul sumei k/(k+1)


𝒌𝟐 +𝟐𝒌+𝟏
Algoritm pentru calculul ∑𝒏𝒌=𝟏 � �
𝒌𝟐 +𝟐𝒌+𝟐

Fig. 33 Calculul sumei ((k^2+2k+1)/(k^2+2k+2))

101
Algoritmică şi programare

Schema logică pentru calcului factorialului (n!)

Fig. 34 Calculul n!

Algoritm pentru calculul 𝑪𝒌𝒏

Fig. 35 Calcul Combinări

102
Algoritmică şi programare

Algoritm pentru calculul 𝑨𝒌𝒏

Fig. 36 Calcul Aramjamente

Algoritm pentru numere prime


Schemă logică pentru testul numărului prim, determinarea şi afişarea
divizorilor și a sumei divizorilor unui număr natural n nenul.

Fig. 37 Algoritm numere prime

103
Algoritmică şi programare

Scheme logice. Subscheme si proceduri


Schema logică pentru citirea unui număr natural nenul, determinarea
și afișarea factorialului numărului utilizând o subschema logică.

Fig. 38 Calculul factorialului utilizând subscheme logice


Schema logică pentru citirea unui număr natural nenul, determinarea
și afișarea factorialului numărului utilizând o procedură.

Fig. 39 Calculul factorialului utilizând o procedură

104
Algoritmică şi programare

Schemă logică pentru citirea unui număr natural nenul,


determinarea și afișarea cifrelor sale
Soluție: Fie n numărul citit. Se determină ultima cifră a numărului n
calculând restul împărțirii lui n la 10. Apoi se atribuie numărului valoarea
câtului împărțirii lui n la 10. Se repeta pașii de mai sus, până când n are
valoarea 0.

Fig. 40 Citirea unui număr natural nenul, determinarea și afișarea cifrelor sale

105
Algoritmică şi programare

Schemă logică pentru citirea unui număr natural nenul și afișarea


inversului numărului

Fig. 41 Determinarea și afișarea inversului unui număr

Test palindrom pentru un număr natural nenul, citit de la tastatura

Fig. 42 Test palindrom

106
Algoritmică şi programare

Mulțimea de palindromuri pana la n

Fig. 43 Mulțimea palindromurilor pana la n

Mulțimea de palindromuri dintr-un interval

Fig. 44 Mulțimea de palindromuri dintr-un interval

107
Algoritmică şi programare

Schema logică pentru citirea unui număr natural nenul,


determinarea și afișarea sumei/produsului cifrelor sale

Fig. 45 Suma/produsul cifrelor unui număr natural nenul, citit de la tastatură

Schema logică pentru afișarea produsului cifrelor unui număr


natural, utilizând o subschemă logică

Fig. 46 Produsul cifrelor unui număr natural, utilizând o subschemă logică

108
Algoritmică şi programare

Schema logică pentru afișarea produsului cifrelor unui număr


natural, utilizând o procedură

Fig. 47 Produsul cifrelor unui număr natural, utilizând o procedură

Schema logică și pseudocod pentru calculul sumei/produsului


cifrelor unui număr natural nenul utilizând proceduri.

Fig. 48 Produsul/suma cifrelor unui număr natural, utilizând proceduri

109
Algoritmică şi programare

Aranjamente și combinări utilizând o subschemă logică

Fig. 49 Aranjamente și combinări utilizând o subschemă logică

Sisteme de numerație. Conversia unui număr din binar (baza 2) în


zecimal (baza 10)

Fig. 50 Conversia unui număr din baza 2 în baza 10

110
Algoritmică şi programare

Conversia unui număr din zecimal (baza 10)în binar (baza 2)

Fig. 51 Conversia unui număr din baza 10 în baza 2

Scheme logice. Tablouri


Algoritm pentru scrierea si citirea elementelor unui vector
Pas1. Stabilirea numărului de elemente ale vectorului.
Algoritm: citește n

/* nr de elemente*/
start
do
read n
while n<=1
enddo

/* nr de elemente*/
start
read n
while n<=1
read n
Fig. 52 Citește n
endwhile

111
Algoritmică şi programare

Pas2. inițializarea și afișarea elementelor vectorului. Algoritm: citire si


scriere elemente vector

/* citirea elementelor*/

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


read a[i]
endfor

/* scrierea elementelor*/

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


write a[i]
endfor Fig. 53 Vector. Citire și scriere elemente

Pas 3. Algoritm pentru scrierea și citirea elementelor vectorului

Fig. 54 Algoritm pentru citirea și scrierea elementelor vectorului

Algoritm pentru:
• Suma elementelor vectorului aflate pe poziții pare;
• Produsul elementelor vectorului aflate pe poziții impare;
• Suma elementelor vectorului cu valoare para ;
• Produsul elementelor vectorului cu valoare impara;

112
Algoritmică şi programare

Fig. 55 Algoritm pentru calculul sumei/produsului elementelor unui vector

Algoritm pentru elementele comune a doi vectori

Fig. 56 Elementele comune a doi vectori

Algoritm pentru sortarea elementelor vectorului

Fig. 57 Sortare elemente vector (crescătoare)

113
Algoritmică şi programare

Matrice. Algoritm (pseudocod si schema logică) pentru scrierea si


citirea elementelor

Fig. 58 Scriere si citire elemente matrice


Într-o matrice pătratică, numărul de linii= numărul de coloane (n=m).
Diagonala principala:
elementele a[i][i], cu i=1,n sau a[i][i], cu i=0,n-1
Diagonala secundara:
elementele a[i][n-i+1], cu i=1,n sau a[i][n-i-1], cu i=0,n-1
Zonele determinate de diagonale:
I. Pe diagonala principala: i=j
Sub diagonala principala: i>j
Deasupra diagonalei principale: i<j
II. Pe diagonala secundară: j=n-i+1
Sub diagonala secundara: j>n-i+1
Deasupra diagonalei secundare: <n-i+1
Schemă logică și pseudocod pentru citirea/scrierea elementelor
matricei, suma elementelor diagonalei principale si secundare
Pas1. Stabilirea numărului de linii si coloane ale matricei

Start
do
read n
while (n<=1)
enddo

114
Algoritmică şi programare

Pas2. Citirea/scrierea elementelor matricei.

//citire elem. matrice

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


for (j=1; j<=n; j++)
read a[i,j]
endfor
endfor

//scriere elem. matrice

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


for (j=1; j<=n; j++)
write a[i,j]
endfor
endfor

Pas3. Suma elementelor DP si DS

//Suma elem. DP matrice


for (i=1; i<=n; i++)
sp+= a[i,i] //sau sp = sp
+ a[i,i]
write sp
endfor

//Suma elem. DS matrice


for (i=1; i<=n; i++)
ss+=a[i,n+1-i] ] //sau
ss = ss + a[i, n+1-i]
write ss
endfor

Pseudocod citire/scriere elemente matrice, suma elementelor


diagonalei principale si secundare
Start
do //stabilire nr linii si coloane matrice
read n
assign n<- abs(n)
while (n<=1)

115
Algoritmică şi programare

enddo

for (i=1; i<=n; i++) //citire elem. matrice


for (j=1; j<=n; j++)
read a[i,j]
endfor
endfor

for (i=1; i<=n; i++) //scriere elem. matrice


for (j=1; j<=n; j++)
write a[i,j]
endfor
endfor

for (i=1; i<=n; i++) //Suma elem. DP matrice


sp+= a[i,i] //sau sp = sp + a[i,i]
write s
endfor

for (i=1; i<=n; i++) //Suma elem. DS matrice


ss+=a[i,n+1-i] //sau ss = ss + a[i, n+1-i]
write ss
endfor
End

Fig. 59 Citire /scriere elemente matrice, suma elementelor diagonalei principale si


secundare

116
Algoritmică şi programare

Algoritm sortare elemente matrice pe linia k

Fig. 60 Sortare elemente matrice pe linia k

117
Algoritmică şi programare

Limbajul de programare C/C++


Noțiuni introductive
Dezvoltat la începutul anilor 1970 de Ken Thompson și Dennis
Ritchie pentru a scrie nucleul sistemului de operare UNIX, Limbajul de
programare C reprezintă un limbaj standardizat care aparține clasei
limbajelor de nivel scăzut sau de nivel mediu.
Popularitatea Limbajului de programare C se datorează portabilității și
eficienței codului obiect pe care îl poate genera, fiind unul din cele mai
cunoscute limbaje de programare utilizate pentru scrierea de software de
system, dar și pentru faptul că este implementat pe majoritatea platformelor
de calcul existente.
Ulterior apariției limbajului C au fost lansate diferite versiuni și
extinderi ale limbajului: C++, C#, Java, Javascript, etc.
Spre deosebire de majoritatea limbajelor de programare, limbajul C
este asimilat deseori unui "asamblor portabil" datorită relației strânse dintre
inter-operabilitate și echipamentul hardware, astfel, programul sursă C poate
fi compilat și executat pe aproape orice tip de calculator, similar altor
limbaje de programare, în timp ce limbajele de asamblare presupun un
anumit tip de mașină de calcul.
Limbajul C, prin prisma paradigmei programării procedurale, a fost
conceput pentru realizarea cât mai facilă a programelor de dimensiuni mari
sau foarte mari, într-un mod structurat.
Caracteristici ale limbajului C:
• Orientat spre paradigma programării procedurale;
• Utilizează un limbaj preprocesor pentru lucrul cu macrouri și
fișiere sursă;
• Limbaj facil, cu funcționalități de baza: biblioteci, pointeri,
funcții predefinite sau utilizator, pointeri la funcții și fișiere;
• Întrebuințează un set simplu de tipuri de date;
• Variabilele trebuie declarate (local sau global);
• Permite lucrul cu adrese de memorie datorită pointerilor;
• Permite folosirea parametrilor și pointerilor în cadrul
funcțiilor;
• Structuri și uniuni, definite
de utilizator.

Brian Kernighan și Dennis Ritchie,


în prima lor carte, a dat un exemplu
standard de program introductiv, utilizat

118
Algoritmică şi programare

apoi în majoritatea cărților de programare, indiferent de limbajul de


programare. Programul afișează „Hello, World!“ la ieșirea standard
(terminal sau monitor).
Programul prezentat va fi compilat fără erori de marea majoritate a
compilatoarelor moderne. Totuși, dacă va fi compilat de un compilator ce
respectă standardul ANSI C, va produce unele mesaje de avertizare. Mai
mult, în urma compilării programului sursă, vom avea mesaje de eroare,
dacă se respectă standardele C99, deoarece variabila de ieșire, de tip int, nu
va putea fi dedusă dacă nu a fost specificată în codul sursă (tipul de data
returnat de funcția main).
Aceste mesaje de avertizare sau eroare sunt eliminate operând
schimbări minore în program.
//directiva preprocesor
//definirea funcției main() fără argument,
care returnează o valoare de tip întreg.
//executarea codului pentru funcția de
afișare printf
//terminarea execuției funcției main,
returnând o valoare întreagă, interpretată de
către sistem ca un cod de ieșire pentru o
execuție efectuată cu succes.

Structura unui program C/C++


#include<...> • Directivele preprocesor (#include<stdio.h, conio.h>,
etc) - conțin antetele funcțiilor din program
main() • Orice program C conține din una sau mai multe
{ funcții care realizează operațiile de calcul sau de
declarații, intrare /ieșire
apeluri de • În corpul unei funcții întâlnim declarații, apeluri ale
funcții, altor funcții și instrucțiuni.
instrucțiuni,
}
Orice program C/C++ utilizează biblioteci 1, care permit utilizarea
funcțiilor pe care le conțin.

1
funcții biblioteca (printf, scanf, getch)

119
Algoritmică şi programare

De exemplu, funcția de bază printf, care afișează pe ecran, este


definită în fișierul antet (header) stdio.h
Pentru a putea executa comanda printf în programul nostru, trebuie să
adăugăm următoarea directiva preprocesor #include <stdio.h>, care
formează prima noastră linie de cod.
Orice program C/C++ are în componență cel puțin o funcție (funcția
main) și variabile. Funcția main () este aparte deoarece execuția
programului începe întotdeauna cu această funcție. Orice funcție conține
parametri și expresii (zero sau mai mulți/multe). O expresie este construită
din operanzi conectați prin operatori conform anumitor reguli sintactice.
Definiția unei funcții include următoarele componente: un tip de retur,
un nume, parametri (între paranteze rotunde) și un corp al funcției numit și
bloc (cuprins între acolade).
Dintre funcțiile de intrare/ieșire cel mai des utilizate sunt printf și
scanf.
• Printf - este o funcție de scriere pe terminal.
• Scanf - citește date de la tastatură și le scrie în memoria
interna.
Unități lexicale C/C++
Unitățile lexicale ale unui program C/C++ sunt alcătuite din caractere,
fiecare caracter având un anume înțeles pentru compilatorul C/C++,
folosirea incorectă a acestora fiind semnalată prin mesaje de eroare.
Limbajul de programare C nu prevede tipul da dată Boolean. El poate
fi definit sub forma:
#define BOOL char
#define FALSE 0
#define TRUE 1
Mulțimea caracterelor
Mulțimea caracterelor ce pot fi folosite în programe C/C++ este
împărțită în următoarele submulțimi:
• caractere alfanumerice: literele mici și mari din alfabetul englez
precum și cifrele;
• caractere speciale
• caractere de spațiere
• alte caractere: celelalte caractere tipăribile din setul de caractere
ASCII

120
Algoritmică şi programare

Caracter Denumire Caracter Denumire


, virgulă . punct
: două puncte ; punct și virgulă
? semnul întrebării ! semnul exclamării
' apostrof " ghilimele
( paranteză rotundă ) paranteză rotundă
stânga dreapta
[ paranteză dreaptă ] paranteză dreaptă
stânga dreapta
{ acoladă stânga } acoladă dreapta
< mai mic > mai mare
^ săgeată sus = egal
+ plus - minus
* asterisc / slash
% procent \ backslash
| bară verticală ~ tildă
_ underscore # diez
& ampersand
Tabel 5 Caractere speciale

Cod Denumire Semnificație


ASCII
32 spațiu un spațiu
9 Tab grup de spații (tabulator orizontal)
10 line feed trecere la linia următoare (new line)
13 carriage revenire la începutul liniei curente (folosit în
return special la imprimante)
11 tab vertical tabulator vertical (folosit în special la imprimante)
12 form feed trecere la pagina următoare (folosit în special la
imprimante)
Tabel 6 Caractere de spațiere

Comentariul
Un comentariu este o porțiune de text care este ignorată de către
compilatorul C/C++. Un comentariu pe o singură linie începe după o
secvență dublu-slash // și se termină la finalul liniei curente.
Un comentariu pe una sau mai multe linii începe după o secvență
slash-star /* și se termină la prima secvență star-slash */. Pentru o mai

121
Algoritmică şi programare

ușoară înțelegere/depanare a programelor se recomandă folosirea


comentariilor cât mai detaliate.
De exemplu, o secvență de program care calculează suma a două
numere poate fi comentată astfel:
aria= b*i; // calculăm aria dreptunghiului
sau
aria= b*i; /* calculăm aria dreptunghiului */
Tipuri de date primare
Un tip de date reprezintă o informație asociată unei expresii pe baza
căreia compilatorul limbajului C/C++ determină în ce context poate fi
folosită expresia respectivă. (char – înseamnă ca se pot efectua operații de
concatenare).
Orice expresie validă are asociată un anumit tip de date. Limbajul
C/C++ definește patru tipuri de date fundamentale: caracter (char), întreg
(int), real (sau flotant - float) și flotant dublu (double), unele dintre acestea
având mai multe variante (denumite subtipuri).
Tipurile char și int reprezintă valori cu sau fără semn, dar
programatorul poate să folosească scurt sau lung, dar programatorul poate să
folosească modificatorii short și long pentru a selecta subtipul dorit. În
anumite situații (foarte rare), tipul flotant dublu (double) este echivalent cu
flotant simplu (float).
Limbajul C/C++ dispune la bază de patru tipuri de date : char, int,
float, double.
• int modificatorii signed și unsigned pentru a selecta un anume
subtip. De asemenea, implicit tipul int poate fi
o Rezervă 2 octeți.
o Se folosește pentru a declara variabile întregi pozitive sau
negative.
o Valoarea minimă este 32768, valoarea maximă 32767.
• char
o Rezervă un singur octet.
o Se folosește pentru a reține caractere din setul local de
caractere (coduri ASCII).
o Valorile minime și maxime care pot fi reținute într‑o
variabilă de tip caracter sunt ‑127 și 127.
• float
o Pentru o variabilă simplă precizie se alocă 4 octeți.
o Se folosește pentru a declara variabilele reale (în virgulă
mobilă). Variabilele în virgulă flotantă pot fi în simplă
precizie sau dublă precizie.

122
Algoritmică şi programare

o Valorile minime și maxime pe care le poate lua variabila:


3.4E 38 și 3.4E+38.
• double
o Se folosește pentru a declara variabile în dublă precizie.
Rezerva 8 octeți.
o Valorile minime și maxime sunt: 1.7E-308 și 1.7E+308.
La tipurile de date prezentate mai sus se pot aplica calificatorii: short,
long, unsigned;
• short
o Calificatorul short se aplică numai la int. El are
semnificația de întreg scurt adică lungimea minimă pe care
se poate reprezenta un întreg. In anumite variante de C,
tipul de data short are aceeași semnificație ca și int.
• long
o se aplică și pentru tipul int și pentru tipul float sau double.
Pentru tipul int, long va reprezenta un întreg pe 4 octeți
(Valorile unui long int: valoarea maximă 2147483647).
o Pentru float, long float este de fapt double iar long double
se va reprezenta pe 10 octeți.
• unsigned
o (fără semn) are semnificația de număr pozitiv sau nul.
o un întreg (int) declarat unsigned va avea valori cuprinse
între 0 și 65535 iar un char declarat unsigned va avea
valori cuprinse între 0 și 255.

Tip/subtip Mărime (octeţi) Domeniu de valori


unsigned 1 0…255
char
signed char 1 -128…127
Char 1 depinde (cu sau fără semn)
short signed 2 -32768…32767
int
short 2 0…65535
unsigned int
short int 2 depinde (cu sau fără semn)
long signed 4 -2147483648…2147483647
int
long 4 0… 4294967296
unsigned int
long int 4 depinde (cu sau fără semn)

123
Algoritmică şi programare

Tip/subtip Mărime (octeţi) Domeniu de valori


signed int 2 sau 4 depinde (cu semn)
unsigned int 2 sau 4 depinde (fără semn)
int 2 sau 4 depinde (cu sau fără semn)
float 4 -3,40×1038...3,4×1038
double 8 -1,79×10308...1,79×10308
Tabel 7 Tipuri de date primare

Constante
Constantele reprezintă cel mai simplu tip de operanzi, mai exact o
valoare care nu poate fi modificată şi care are asociat un tip (stabilit în
funcție de modul de scriere al valorii constantei).
Constante de tip caracter
O constantă de tip caracter se scrie în program între apostrofuri (e.g.
’C’, ’0’, ’.’, ’!’). Tipul constantelor caracter este întotdeauna char.
Pentru a scrie constantele caracter apostrof, backslash (\) sau un
caracter care nu apare pe tastatură se folosesc secvențe escape. O secvență
escape este compusă din caracterul backslash (\) urmat de un caracter
normal sau un număr (în baza 8 sau 16). În acest context, semnificația
caracterului sau a numărului de după backslash devine una specială.
Secvenţă
Semnificaţie
escape
\a alert – produce un sunt în difuzorul calculatorului
\n new line – trecerea la rândul următor
\r carriage return – revenirea la capătul rândului
\t horizontal tab – deplasarea pe orizontală cu un tab
\v vertical tab – deplasarea pe verticală cu un tab (la imprimantă)
\f form feed – trecerea la pagina următoare (la imprimantă)
\b backspace – deplasarea înapoi a cursorului cu un caracter
\\ backslash – caracterul backslash
\’ single quote – caracterul apostrof
\” double quote – caracterul ghilimele
\ooo octal – caracterul cu codul ASCII ooo (unde ooo este un număr
în baza 8, vezi Anexa 2 – Setul de caractere ASCII
\xhh hexazecimal – caracterul cu codul ASCII hh (unde hh este un
număr în baza 16, vezi Anexa 2
Tabel 8 Constante de tip caracter

124
Algoritmică şi programare

Constante de tip șir de caractere


O constantă de tip șir de caractere este un grup de caractere și
secvențe escape scrise între ghilimele.
“Acesta este un sir de caractere”
Tipul constantelor șir de caractere este char[], adică un vector de
caractere.
Constante de tip întreg
O constantă de tip întreg este un număr întreg scris în baza 8, 10 sau
16. Dacă numărul este scris în baza 8, atunci trebuie să fie prefixat de cifra
zero (012). Dacă numărul este scris în baza 16, atunci trebuie să fie prefixat
de zero urmat de litera x (0xA ).
Subtipul constantei este ales astfel încât domeniu acestuia să conțină
valoarea constantei. Programatorul poate scrie sufixele u (10u - de la
unsigned) și/sau l (10l - de la long) pentru a specifica un subtip anume. Dacă
valoarea constantei nu poate fi memorată ca subtip long signed int sau long
unsigned int, atunci constanta se consideră constantă de tip flotant (float).
Constante de tip real
O constantă de tip flotant este un număr real scris în baza 10, cu sau
fără zecimale, cu sau fără exponent. Exponentul este alcătuit din litera e
urmată de un număr întreg n, cu semnificația că valoarea constantei este
numărul dinaintea exponentului înmulțit cu 10n. Separatorul pentru zecimale
este punctul.
Dacă numărul nu are zecimale sau exponent, atunci trebuie urmat de
un punct pentru a fi considerat real.
Tipul implicit al constantei este tipul double, dar dacă aceasta este
urmată de litera f (de la float), tipul implicit va fi float.
De exemplu, constanta cu valoarea -12,5 și tip double se poate scrie:
12.5 sau 1.25e1. Dacă scriem 12.5f, atunci tipul este float.
Cuvinte cheie
Un cuvânt cheie este o secvență de caractere alfanumerice, cu o
anumită semantică predefinită în limbajul C/C++. Cuvintele cheie sunt
scrise cu litere mici.
Cuvintele rezervate în limbajul C/C++ sunt în număr de 32, prezintă o
semnificație predefinită și nu pot fi folosite în alte scopuri într-un program.
Pentru controlul fluxului dispunem de instrucțiunile decizionale if, if-else și
switch și de instrucțiunile repetitive do, while și for.

125
Algoritmică şi programare

auto break case char


const continue default do
double else enum extern
float for go to if
int Long register return
short Signed sizeof static
struct switch typedef union
unsigned void volatile while
Tabel 9 Cuvinte cheie

Separatori
Delimitarea unităților lexicale dintr-un program se face cu ajutorul
separatorilor. Separatorii acceptați de către limbajul C/C++:
• ( ) - Parantezele rotunde specifică, în cadrul unei funcții, lista
de parametrii sau, precizează ordinea de efectuare a operațiilor
pentru evaluarea unei expresii.
• { } - Acoladele delimitează instrucțiunile compuse, numite și
blocuri.
• [ ] - Parantezele drepte specifică dimensiunile tablourilor.
• " " - Ghilimelele delimitează șirurile de caractere
• ' ' - Apostroful încadrează un singur caracter
• ; - instrucțiunea vida. Fiecare instrucțiune se încheie cu ;
• /* */ sau //- Comentariul sau instrucțiunea de documentare.
Variabile
Domeniul de aplicare reprezintă o regiune a programului în care o
variabilă definită poate exista iar dincolo de aceasta regiune variabila nu
poate fi accesata.
In orice limbaj de programare, exista 3 zone unde variabilele pot fi
declarate:
• În afara tuturor funcțiilor (variabile globale)
• În interiorul unei funcții sau a unui bloc (variabile locale)
• În definiția parametrii funcțiilor(parametri formali)
O variabila globala poate fi accesata de orice funcție și doar in
interiorul unei funcții sau unui bloc (unde este declarată).

126
Algoritmică şi programare

Expresii. Operanzi. Operatori


Expresii
O expresie este o secvență de operanzi și operatori. Un operand poate
fi o constantă, o variabilă sau o altă expresie. O expresie este caracterizată
de o valoare și un tip. Valoarea și tipul expresiei sunt determinate de
operatorii și operanzii care formează expresia.
În limbajul C/C++, o expresie are valoarea logică de adevăr
„adevărat” dacă are valoare nenulă (!=) și are valoarea logică de adevăr
„fals” dacă are valoare nulă.
Operanzi
Un operand poate fi: o constantă, o constantă simbolică, un
identificator de variabilă simplă, un identificator de tablou, un element de
tablou, un identificator de structură, un membru al unei structuri, numele
unei funcții, un apel de funcție sau o expresie.
Conversii implicite de tip
În timpul evaluării unei expresii, tipul unei subexpresii poate să fie
schimbat automat în alt tip cu domeniul de valori mai mari. Această operație
se numește conversie implicită de tip și realizează astfel:
• orice operand de tipul char se convertește la tipul int;
• orice operand de tipul short se convertește la tipul int.

În plus, unii operatori prelucrează informațiile stocate în doi operanzi.


Dacă operanzii au același tip, atunci rezultatul operatorului are același tip cu
al operanzilor. Altfel, unul dintre operanzi se convertește la tipul celuilalt,
conform următoarelor reguli:
• dacă un operand este de tipul double atunci și celălalt operand
se convertește la tipul double;
• dacă un operand este de tipul float atunci și celălalt operand se
convertește la tipul float;
• dacă un operand este de tipul unsigned long atunci și celălalt
operand se convertește la tipul unsigned long;
• dacă un operand este de tipul long atunci și celălalt operand se
convertește la tipul long.
Operatori
Limbajul C/C++ dispune de un set puternic de operatori. Operatorii se
pot clasifica după aritate, după asociere sau după prioritatea la evaluare într-
o expresie. Din punct de vedere al arității (adică numărul de operanzi),
operatorii sunt unari, binari și ternari.

127
Algoritmică şi programare

Din punct de vedere al asociativității, operatorii pot fi asociativi de la


stânga la dreapta sau de la dreapta la stânga. Cu excepția operatorilor de
atribuire, operatorului condițional și operatorilor unari, restul operatorilor au
asociativitate de la stânga la dreapta.
În limbajul C/C++, sunt definite 15 niveluri de precedență; nivelul 1
este cel mai prioritar, iar nivelul 15 este cel mai puțin prioritar. Operatorii
aflați pe un anumit nivel de prioritate au aceeași prioritate.

Fig. 61 Niveluri de precedență

128
Algoritmică şi programare

Operatori de adresare
Operatorul de apelare „( )” este folosit ca separator al listei de
parametri a funcțiilor sau ca modificator de prioritate în evaluarea
expresiilor.
Operatorul de indexare „[ ]” este folosit pentru accesarea valorilor din
tablouri de date și va fi discutat în capitolul dedicat tablourilor.
Operatorii de selecție directă „.” și de selecție indirectă „->” se
folosesc în gestiunea structurilor și uniunilor.
De exemplu, instrucțiunea:
printf("Elementul 4 din vectorul v este %d", v[3]); folosește funcția
printf pentru a afișa un mesaj.
Operatori unari
Operatorul de negare logică are rezultatul 1 dacă valoarea operandului
este nulă și 0 dacă valoarea operandului este nenulă.
Exemplu, fie secvența:
int a = 21,b;
b=!a; // b este 0 deoarece a e diferit de 0
b=!b; // b devine 1 deoarece b este 0
Operatorul de negare la nivel de bit schimbă toți biții operandului din
0 în 1 și din 1 în 0. Operatorul de semn plus are ca rezultat valoarea
operandului, iar operatorul de semn minus are ca rezultat valoarea cu semn
schimbat a operandului.
De exemplu,
int a = 33, b, c;
b = -a;
c = +b;
Operatorii de adresă și de adresare indirectă, având sintaxa &operand,
respectiv, *operand, sunt folosiți pentru operații cu adrese de memorie.
Operatorii de incrementare și decrementare au fiecare două forme:
prefixată ++operand și postfixată operand++, respectiv, –operand și
operand--. În forma prefixată valoarea operandului se mărește cu 1 (adică se
incrementează), respectiv se micșorează cu 1 (adică se decrementează), iar
rezultatul expresiei este noua valoare. În forma postfixată, valoarea
operandului se incrementează, respectiv decrementează, iar rezultatul
expresiei este vechea valoare.
Operatorul sizeof are ca rezultat dimensiunea în octeți a operandului
(adică numărul de octeți necesari pentru memorarea unei informații de acest
tip). Operandul poate să fie o expresie sau un tip de dată. Dacă operandul
este o expresie, atunci se evaluează expresia și sizeof calculează
dimensiunea în octeți pentru tipul rezultatului.

129
Algoritmică şi programare

De exemplu,
long b;
char c;
b=sizeof(c); // b = 1 (un caracter ocupa 1 octet)
b=sizeof(-b);// b = 4 (un long int ocupa 4 octeți)
Operatorul de conversie explicită (denumit operator cast) este folosit
atunci când este necesară conversia unei valori la un anumit tip. Sintaxa
operatorului este: (tip)expresie, unde tip este tipul spre care se convertește
valoarea expresiei. De exemplu, operatorul de împărțire aplicat la doi
operanzi întregi efectuează împărțirea întreagă (fără zecimale); pentru a
calcula valoarea reală unul dintre operanzi trebuie transformat în tipul float
sau double:
int a = 6, b = 10;
float f;
f=a/b; // f=6/10 = 0 (împărțire întreagă)
f=(float)a/b;// f=6.0/10 = 0.6 (împărțire reala)
Operatori multiplicativi
Operatorul de înmulțire are sintaxa operand1*operand2 și rezultatul
este produsul valorilor celor doi operanzi, cu eventuale conversii de tip.
Operatorul de împărțire are sintaxa operand1/operand2 și rezultatul
este câtul împărțirii primului operand la cel de-al doilea operand, cu
eventuale conversii de tip.
Operatorul modulo are sintaxa operand1%operand2, se aplică numai
operanzilor de tip întreg și rezultatul este restul împărțirii primului operand
la cel de-al doilea operand. Operandul 2 trebuie să fie nenul.
int a = 3, b = 6;
double c = 8.1, d = 1.1;
printf("%d\n", a * b);// Valoarea 18 de tip întreg
printf("%f\n", a * d);// Valoarea 6.6 de tip real
printf("%f\n", c * d);// Valoarea 8.91 de tip real
printf("%d\n", b / a);// Valoarea 2 de tip întreg
printf("%f\n", c / a);// Valoarea 2.7 de tip real
printf("%f\n", c / d);// Valoarea 7.36 de tip real
printf("%d\n", a % b);// Valoarea 3 de tip întreg
printf("%d\n", b % a);// Valoarea 0 de tip întreg

130
Algoritmică şi programare

Operatori aditivi
Operatorul de adunare are sintaxa operand1+operand2 și rezultatul
este suma valorilor celor doi operanzi, cu eventuale conversii de tip.
Operatorul de scădere are sintaxa operand1–operand2 și rezultatul este
diferența dintre valoarea primului și a celui de-al doilea operand, cu
eventuale conversii de tip.
Pentru exemplificare considerăm secvența următoare:
int a = 3, b = 5;
double c = 8.12, d = 1.1;
printf("%d\n", a + b);// Valoarea 8 de tip întreg
printf("%d\n", a – b);// Valoarea -2 de tip întreg
printf("%f\n", c – b);// Valoarea 3.12 de tip real
printf("%f\n", a + c);// Valoarea 11.12 de tip real
printf("%f\n", c + d);// Valoarea 9.21 de tip real
printf("%f\n", c – d);// Valoarea 7.02 de tip real
Operatori pentru deplasare
Operatorii de deplasare se aplică operanzilor de tip întreg, rezultatul
fiind de tip întreg.
Operatorul de deplasare stânga are sintaxa: operand1<<operand2 și
rezultatul se obține prin deplasarea la stânga a configurației binare a
primului operand, cu numărul de biți dat de valoarea celui de-al doilea
operand. Pozițiile binare rămase libere în dreapta se completează cu zerouri.
Operatorul de deplasare dreapta are sintaxa: operand1>>operand2 și
rezultatul se obține prin deplasarea la dreapta a configurației binare a
primului operand, cu numărul de biți dat de valoarea celui de-al doilea
operand. Pozițiile binare rămase libere în stânga se completează cu bitul de
semn pentru subtipurile cu semn (e.g. int, short, etc.), sau cu 0 pentru
subtipurile fără semn (e.g. unsigned int, unsigned short, etc.).
Operatori relaționali
Operatorii relaționali sunt folosiți pentru tipurile aritmetice de date și
pentru datele de tip pointer, având sintaxa operand1 operator operand2.
Valoarea expresiei se obține astfel: se evaluează cei doi operanzi; dacă
valoarea primului operand este în relația dată de operator, cu al doilea
operand, rezultatul expresiei este 1(true); altfel rezultatul expresiei este
0(false).
De exemplu:
int a = 20, b = 13, c;
c = a < b; // c este 0
c = a <= b; // c este 0

131
Algoritmică şi programare

c = a > b; // c este 1
c = a >= b; // c este 1
Operatori de egalitate
Operatorii relaționali sunt folosiți pentru tipurile aritmetice de date și
pentru datele de tip pointer, având sintaxa operand1 operator operand2.
Valoarea expresiei se obține astfel: se evaluează cei doi operanzi; dacă
valoarea primului operand este în relația dată de operator, cu al doilea
operand, rezultatul expresiei este 1 (true); altfel rezultatul expresiei este 0.
De exemplu, fie secvența:
int a = 20, b = 13, c;
c = a == b; // c este 0
c = a != b; // c este 1
Operatori logici
Operatori logici binari se aplică valorilor operanzilor conform funcției
descrise anterior. Evaluarea se realizează prin scurt-circuit, adică evaluarea
operanzilor se oprește atunci când valoarea expresiei a fost stabilită și nu
mai poate fi schimbată de restul expresiei.
De exemplu:
short int a = 20, b = 0, c, d;
c = a && b; // c este 0 (deoarece b este 0)
d = a || b; // d este 1 (deoarece a este 20)
Operatorul condițional
Operatorul condițional este singurul operator ternar al limbajului
C/C++. Sintaxa sa este: operand1?operand2:operand3. Valoarea expresiei
se obține astfel: se evaluează expresia operand1; dacă valoarea acesteia este
nenulă (true) rezultatul expresiei este valoarea lui operand2, iar operand3
nu se mai evaluează; altfel, dacă valoarea expresiei operand1 este nulă
(false) rezultatul expresiei este valoarea lui operand3, iar operand2 nu se
mai evaluează. (max=a > b ? a : b;)
Operatori de atribuire
Operatorii de atribuire sunt de două tipuri: simpli și compuși.
Operatorul de atribuire simplă are sintaxa var=expr unde var este o variabilă
sau un element dintr-un tablou, iar expr este o expresie. De exemplu,
considerăm secvența:
int a, b;
char c;
double d;
a = 5; // a este 5

132
Algoritmică şi programare

d = 12.21; // c este 12.21


b = a + 3.5; // b este 8 (se face conversie la int)
b = b+d; // b este 20 (se face conversie la int)
Operatorul virgulă
Operatorul virgulă este folosit pentru scrierea unei expresii formată
dintr-o listă de expresii, având sintaxa: operand1, operand2, ..., operandn.
Valoarea expresiei se calculează astfel: se evaluează succesiv valorile lui
operand1, operand2, ..., operandn.
Tipul și valoarea întregii expresii este tipul și valoarea expresiei
operandn.

De exemplu,
int a, b, c;
a = 2, b = 5, c = b % 2;

Întâi se atribuie variabilei a valoarea 2, apoi se atribuie variabilei b


valoarea 5 și în final variabilei c restul împărțirii valorii lui b la 2, adică se
atribuie variabilei c valoarea 1. Valoarea expresiei a = 2, b = 5, c = b % a
este 1 (valoarea expresiei c=b % a), iar tipul ei este int (tipul expresiei c=b
% a).

133
Algoritmică şi programare

Structuri algoritmice. Instrucțiuni


Prelucrările realizate de un program corespund algoritmului
implementat, utilizând instrucțiuni.
Instrucțiunea se încheie cu ajutorul separatorului punct și virgulă
numit și instrucțiunea vidă (;).
Instrucțiunile limbajului C/C++ pot fi împărțite în următoarele
categorii:
• Expresii,
• Blocuri,
• Selecții,
• Iterații,
• Salturi.

Algoritmul, conform teoremei Bohm-Jacopini, poate fi conceput cu


ajutorul a trei structuri fundamentale de calcul: structura secvențială
(secvența), structura alternativă decizională (decizia sau selecția) și structura
repetitivă (bucla sau ciclul).
Clasa structurilor secvențiale (enunțuri): start, end, read v, write v,
assign v  e.
Clasa structurilor alternative:
• structura alternativă cu două alternative
• structura alternativă cu n alternative.
Clasa structurilor repetitive:
• ciclul cu test inițial,
• ciclul cu test final,
• ciclul cu inițializare, test și reinițializare.

Limbajul C/C++ are relativ puține instrucțiuni în comparație cu alte


limbaje de nivel înalt.
Astfel, instrucțiunile READ, WRITE, OPEN, CLOSE etc. prezente în
majoritatea limbajelor, nu există în limbajul C/C++, aceste operații de
intrare/ieșire trebuie realizate explicit prin funcții utilizator sau funcții de
bibliotecă.
Limbajul C/C++ permite scrierea programelor după descrierea unui
algoritm prin scheme logice bine structurate sau prin limbaj pseudocod
datorită instrucțiunilor de selecție (if‑else, switch), repetitive (while, do, for)
și posibilităților de grupare a instrucțiunilor în blocuri.

134
Algoritmică şi programare

Instrucțiuni simple
Instrucțiunile simple pot fi declarații de variabile sau instrucțiuni
expresie. Fiecare instrucțiune simplă se termină obligatoriu prin simbolul ';'
(punct și virgulă).
Declarațiile de variabile sunt instrucțiuni în care se declară tipul și,
eventual, valoarea inițială a unor variabile.
Instrucțiunile expresie sunt construcțiile (terminate cu punct și
virgulă) în care identificatorii, constantele și apelurile de funcție sunt legate
prin operatori
Instrucțiunile expresie sunt expresiile de atribuire, de incrementare
/decrementare sau de invocare de metodă, care, la rândul lor, se termină prin
simbolul ';' (punct și virgulă).
expresie;
Exemplu :
a = b = 0;
n ++;
gets (t);
r = ((d = b * b – 4 * a * c) > 0 ? sqrt (d) : sqrt( d));
Instrucțiuni compuse
Structurile de control (structurate) se mai numesc și instrucțiuni
compuse. Acestea au rolul de a indica succesiunea în care se execută
instrucțiunile programului.
Conform celor afirmate anterior, dispunem de următoarele structuri de
control: blocul, instrucțiunile de ramificare (decizia), instrucțiunile
repetitive (bucla sau ciclul) și structura de tratare a excepțiilor.
Blocul
Blocul reprezintă o succesiune de instrucțiuni simple sau compuse,
cuprinse între acolade, ({}). În particular, un bloc poate conține alte blocuri.
Variabilele declarate într-un bloc sunt valabile numai în blocul
respectiv, din locul declarării variabilei până la sfârșitul blocului, inclusiv în
blocurile interioare.
Din punct de vedere sintactic o instrucțiune compusă este echivalentă
cu o singură instrucțiune și de aceea la descrierea sintaxei instrucțiunilor
while, if, do etc. unde apare instrucțiunea, se va subînțelege că este o
instrucțiune simplă sau un bloc de instrucțiuni.
Obs: Nu se pune terminatorul de instrucțiune „;“ după acolada dreaptă
de la sfârșitul blocului.

135
Algoritmică şi programare

Structuri decizionale(ramificate)
Structurile decizionale sunt acele structuri de control în care fluxul
programului înglobează două sau mai multe ramificații paralele.
În majoritatea limbajelor de programare, structurile ramificate sunt
realizate prin instrucțiunile decizionale (de selecție) if, if-else și switch.
Instrucțiunea if
Instrucțiunea if este o instrucțiune
condițională, de forma:

if(condiție)
instrucțiune

în care
condiție - este o expresie logicăș
instrucțiune - este o instrucțiune simplă sau compusă, care se
execută dacă și numai dacă expresia condiție are valoarea true.
Instrucțiunea funcționează astfel:
• se determină valoarea de adevăr pentru expresie; dacă este
diferită de zero este adevărată, iar dacă este zero este falsă;
• dacă expresie este adevărată, atunci se execută instrucțiune;
• dacă expresie este falsă, atunci nu se execută nimic.
Deoarece o expresie este considerată adevărată dacă este diferită de
zero în limbajul C/C++ în loc de:
if (expresie! = 0)
este suficient să scriem:
if (expresie)
Instrucțiunea if-else
Instrucțiunea if este o
instrucțiune condițională, de forma:

if(condiție)
instrucţiune1
else
instrucţiune2

în care
condiție - este o expresie logică
instrucţiune1 - este o instrucțiune simplă sau compusă, care se
execută dacă și numai dacă expresia condiție are valoarea de adevăr true
(!=0);

136
Algoritmică şi programare

instrucţiune2 - este o instrucțiune simplă sau compusă, care se


execută dacă și numai dacă expresia condiție are valoarea false.
Instrucțiunea funcționează astfel:
• se determină valoarea de adevăr pentru expresie; dacă este
diferită de zero este adevărată, iar dacă este zero este falsă;
• dacă expresie este adevărată, atunci se execută instrucțiune1;
• dacă expresie este falsă, atunci se execută instrucțiune2.

Exemplul
1: maximul a
trei numere a,
b, c

Primul
exemplu este
greșit deoarece
else se asociază
cu al doilea if
nu cu primul așa cum este corect și cum indică și alinierea. Această
ambiguitate este rezolvată în secvența din partea dreaptă utilizând blocul
(acoladele) și astfel asociem corect else la primul if.

Exemplul 2: determină ce fel de triunghi este: echilateral, isoscel sau


scalen (oarecare).

137
Algoritmică şi programare

Instrucțiunea switch
Instrucțiunea switch este o structură de control, cu următoarea formă:
switch (selector)
{
case c 1 : secvență 1
case c 2 : secvență 2
.............................
case c n : secvență n
default: secvență (n+1)
}

în care:
selector - o expresie a cărei valoare are tip întreg sau tip char sau tip
enumerare;
c i - o constantă, sau o expresie a cărei valoare este o constantă, având
același tip cu cel al selectorului;
secvență i - o secvență de instrucțiuni simple sau compuse (poate fi și
vidă).

Instrucțiunea switch este o instrucțiune de decizie multiplă care


ramifică execuția programului către o anumită secvență de instrucțiuni după
cum o expresie este egală cu o anumită valoare constantă.
Efectul instrucțiunii switch este următorul:
1. se evaluează expresia și se compară cu valoarea cazurilor
prezente în etichetele case;
2. dacă valoarea expresiei este egală cu constanta de la unul din
cazuri, execuția continuă de la secvența de instrucțiuni
etichetată cu acel case în jos. Dacă secvența respectivă se
termină cu instrucțiunea break, se execută doar acele
instrucțiuni, astfel execuția continuă cu următoarea secvență
până se întâlnește un break sau sfârșitul instrucțiunii switch.
Această curgere în jos la următoarea secvență poate fi în
avantajul programatorului, dar poate fi și cauza unor erori;
3. dacă valoarea expresiei nu este egală cu nici o constantă de la
nici unul din cazuri, execuția continuă cu secvența de
instrucțiuni etichetată cu default, iar dacă default lipsește
atunci nu se execută nimic;
Instrucțiunea switch se mai poate folosi cu succes și la ramificări
multiple, de exemplu ramificările cerute de un meniu.

138
Algoritmică şi programare

Exemplu:
# include "stdio.h"
void main ()

int k;
..............
printf(" M E N U \n");
printf(" 1 = creare fişier \n");
printf(" 2 = Modificare articol \n");
printf(" 3 = Ştergere articol \n");
printf(" 4 = Adăugare în coadă \n");
printf(" Opţiunea dvs.");
scanf (" %d, &k);
switch (k)

case 1: creare (.....); break;
case 2: modif (.....); break;
case 3: şterg (.....); break;
case 4: adaug (.....); break;
default: exit (1);

................................
...............................

Structuri repetitive (bucle sau cicluri)
Structurile repetitive, numite bucle sau cicluri, sunt structuri de
control în care o anumită instrucțiune simplă sau compusă, de regulă un
bloc, se execută în mod repetat. În limbajele de programare, ciclurile se
realizează prin instrucțiunile while, do-while și for.
Instrucțiunea while
Instrucțiunea repetitivă while, numită și ciclu cu test inițial, are
următoarea formă:

while (condiție)
instrucțiune

în care:
condiție - este o expresie logică;
instrucțiune - este o instrucțiune simplă sau compusă (de regulă un
bloc), care se repetă cât timp expresia condiție are valoarea true.

139
Algoritmică şi programare

Exemplu:
int n = 0; while (1) { int n = 0;
while (n < 10) /* executa la infinit while (1) {
{ instrucțiunea*/ n ++;
n ++; } if (n == 10) {
} break;
}
}
Corpul buclei while se execută cât timp (while cât timp) expresia
este diferită de zero (adevărată) sau să nu se execute niciodată dacă la prima
evaluare expresia este nulă.
De asemenea, while poate defini și o buclă infinită, de exemplu:
while (1)
instrucțiune
urmând ca ieșirea din ciclare să se facă din corpul instrucțiunii
folosind: break, return sau exit().
Instrucțiunea do-while
Instrucțiunea do-while ,numită și ciclu cu test final, are următoarea
formă:

do
bloc/instrucțiune
while(condiție);

în care:
bloc - este o structură de control sub formă de bloc;
condiție - este o expresie logică.
Executarea blocului se repetă și în acest caz cât timp este satisfăcută
condiția dar, spre deosebire de instrucțiunea while, în acest caz testarea
condiției se face după ce a fost executat corpul ciclului.
Se execută corpul buclei, după care se evaluează expresie și dacă este
diferită de zero (adevărată) atunci se reia execuția corpului instrucțiunii,
astfel dacă expresie este zero (falsă) bucla ia sfârșit și se trece la
instrucțiunea următoare de după do. Din modul de executare a instrucțiunii
do se observă că instrucțiune (corpul buclei) se execută cel puțin o data.

140
Algoritmică şi programare

Exemplu:

Instrucțiunea for
Instrucțiunea for se folosește în special atunci când numărul de
repetări al corpului ciclului este dinainte cunoscut. Această instrucțiune are
următoarea formă:

for(inițializare opt ; condiţieContinuare opt ; incrementare opt )


instrucțiune opt

în care:
inițializare: listă de expresii de inițializare, separate prin virgule;
condițieContinuare: expresie logică;
incrementare: listă de expresii, care se execută după executarea
corpului ciclului;
instrucțiune: instrucțiune simplă sau compusă (de preferință un bloc)
care constituie corpul ciclului.

Toate componentele marcate cu opt sunt opționale.


Executarea instrucțiunii for decurge astfel:
• se execută mai întâi secvența de expresii inițializare;
• se intră în ciclu, executându-se în mod repetat instrucțiune
urmată de secvența de expresii incrementare, cât timp expresia
condiţieContinuare are valoarea true;

141
Algoritmică şi programare

• când expresia de condiţieContinuare are valoarea false, se iese


din ciclu.
Exemplu:
int i; int i; int i;
i = 0; for (i = 0; i < 10;) { for (i = 0; i < 10; i++) {
for (; i < 10;) { i++; printf("%d\n", i);
i++; printf("%d\n", i); }
printf("%d\n", i); }
}

Instrucțiunea for extinsa


Instrucțiunea for extinsă este o variantă a instrucțiunii for, care are
forma:

for(tip e:a)
instrucțiune

în care:
a - o structură de date iterabilă, respectiv un tablou, o enumerare sau o
colecție;
e - o variabilă, al cărei tip este același cu tipul elementelor structurii a;
tip - tipul variabilei e;
instrucțiune - o instrucțiune simplă sau compusă (preferabil un bloc).
Semnificația instrucțiunii for extinse este următoarea: pentru fiecare
element e al structurii a se execută instrucțiune. Se permite ca tipul
variabilei e să fie declarat chiar în interiorul parantezei instrucțiunii for.
Utilizarea in cicluri a instrucțiunilor break si continue
Instrucțiunile break și continue sunt instrucțiuni de control, care
produc salt către sfârșitul corpului ciclului. Ele pot fi simple sau cu etichetă.
Instrucțiunea break simplă, produce ieșirea definitivă din ciclul în
corpul căreia se găsește.
Instrucțiunea break etichetă se folosește când există mai multe cicluri
imbricate și provoacă ieșirea definitivă din ciclul marcat cu etichetă.
Instrucțiunea continue provoacă întreruperea executării restului
instrucțiunilor din corpul ciclului la iterația curentă și trecerea la executarea
iterației următoare a ciclului.
Instrucțiunea continue etichetă se folosește când există mai multe
cicluri imbricate. Ea are un efect similar cu continue, dar se referă la corpul
ciclului marcat cu etichetă.
Instrucțiunile break și continue pot fi folosite în toate instrucțiunile
repetitive: while, do-while și for.

142
Algoritmică şi programare

Descriptori de format
Printf("%[-]lungime.precizie cod_format", nume variabila);
unde:
[-] - aliniere la dreapta
lungime.precizie – (nr. cifre) partea intreagă.partea fractionara
cod_format:
• d, i - valori întregi;
• f - reale in simpla precizie 4 octeți (float);
• lf - reale in dubla precizie - long float (double);
• e, E - afișare valori in format științific cu exponent, cu o singura
cifra la partea întreagă;
• c – caracter;
• s – string (sir de caractere);
• p - afișare pointer, adrese de memorie;
• x, X - afișare valori hexazecimale;
• o - afișare valori întregi octale;

Exemplu:
int a, b = 1, c = 2, d = 3, e = 4, x, y;
a = b - c + d * e; /* afișam rezultatul operației: 1-2+3*4 = 11 */
printf("a=%d", a); =>se va afișa pe terminal a=11
Aplicații practice
Citirea (de la tastatura) si scrierea variabilelor

Dupa compilare si executie, se vor afisa: Valorile variabilelor a si b.

143
Algoritmică şi programare

Suma a 2 numere

După compilare si execuție, se va afișa: Suma numerelor a si b


Calculați aria cercului, cerând de la tastatura valoarea razei cercului.

Rezolvare utilizând instrucțiunea decizionala if:

144
Algoritmică şi programare

Ecuația de gradul I

145
Algoritmică şi programare

Aria si perimetrul dreptunghiului

Ecuația de gradul II

146
Algoritmică şi programare

Suma primelor n numere (Gauss) si factorialul

Conversie temperatura.
Problema: Afișați corespondentul temperaturii din grade Celsius in
grade Kelvin, Fahrenheit, Rankine si Réaumur
Formule de conversie:
K = C + 273.15;//Kelvin
F = C * 1.8 + 32;//Fahrenheit
Ra = C * 1.8 + 32 + 459.67;//Rankine
Re = C * 0.8;//Réaumur

147
Algoritmică şi programare

Divizorii unui număr

CMMDC

148
Algoritmică şi programare

Masive de date
Masivele de date sunt tablouri de date omogene( de același tip,
standard sau definit de utilizator), reunite sub un singur nume si dispuse
contiguu 2 într-un bloc de memorie. Elementele pot fi accesate individual
prin indici. Toate elementele au un predecesor (excepție primul) si un
succesor (excepție ultimul).
Tabloul ne permite să programăm/efectuam mai ușor operații asupra
grupurilor de valori de același tip. Nu se pot defini tablouri cu componente
de tipuri diferite.
Un tablou poate avea una sau mai multe dimensiuni. Numărul
dimensiunilor tabloului este limitat doar de memoria calculatorului pe care
rulează programul care folosește masive de date.
Dacă numărul de valori folosite la inițializare depășește numărul de
elemente din masiv atunci compilatorul generează un mesaj de eroare. Dacă
numărul de valori folosite la inițializare este mai mic decât numărul de
elemente din masiv, atunci restul valorilor sunt inițializate cu 0.
Masive de date unidimensionale
Un masiv unidimensional se numește vector. Un vector se declară
conform sintaxei:

tip identificator[n];

tip este tipul (predefinit sau utilizator) comun al tuturor elementelor


vectorului;
identificator este numele vectorului;
n este un număr natural nenul care precizează numărul de componente
ale vectorului.
Numărul de elemente/componente se declară între []. Fiecare
componentă a unui tablou poate fi tratată exact ca o variabilă simplă
(identificator[i]). Referirea la componenta i a vectorului se face cu
identificatorul[i]. Această componentă are valoarea memorată la adresa
adr+i*sizeof(tip).
Declarația:

int vector[1000];

2
Care se leagă, se înrudește, se unește cu ceva, care are elemente apropiate, comune cu
altceva

149
Algoritmică şi programare

• creează un tablou cu 1000 de elemente/componente, toate de


tip int.
• primul element are indicele 0, al doilea are indicele 1, iar
ultimul are indicele 999.
• se rezervă o zonă contiguă de memorie de dimensiune
sizeof(int)*1000=4*1000=4000 octeți.
Aplicații practice
Algoritm pentru citirea elementelor unui vector
for(i = 0; i < n; i ++) i=0;
{ while (i < n)
printf("a[%d]=",i); {
scanf("%d",&a[i]); printf("a[%d]=",i);
} scanf("%d",&a[i]);
i ++;
}
Algoritm pentru scrierea elementelor unui vector
for(i = 0; i < n; i ++) i=0;
{ while (i < n)
printf("Elementul a[%d]=%d\n",i,a[i]); {
} printf(“Elementul
a[%d]=%d\n",i,a[i]);
i ++;
}
Algoritm pentru citirea și scrierea elementelor unui vector
#include <stdio.h>
#include<conio.h>
main()
{
int i, n, a[25];
printf("Tastați numărul elementelor vectorului:");
scanf("%d", &n);
for(i = 0;i < n;i ++) // citire/inițializare elemente vector
{
printf("a[%d]=",i);
scanf("%d", &a[i]);
}
printf("Elementele vectorului sunt:\n");
for(i=0;i<n;i++)// afișare elemente vector

150
Algoritmică şi programare

printf("a[%d]=%d\n",i,a[i]);
system("pause");
return 0;
}
Suma elementelor unui vector
#include <stdio.h>
#include<conio.h>
int s=0,i,n,a[25];
main()
{printf("Stabiliți numărul de elemente ale vectorului, n<=25:");
scanf("%d", &n);
for(i = 0;i < n;i ++)// inițializare elemente vector
{
printf("a[%d]=",i);
scanf("%d",&a[i]);
}
printf("Elementele vectorului sunt:\n");
for(i=0;i<n;i++)// afisare elemente vector
printf("a[%d]=%d\n",i,a[i]);
//suma elemente vector
for(i=0;i<n;i++)
s=s+a[i];
printf("Suma elementelor vectorului=%d\n",s);
//system("pause");
return 0;}

sau

#include <stdio.h>
#include<conio.h>
int s=0,i,n,a[25];
main()
{printf("Stabiliți numărul de elemente ale vectorului, n<=25:");
scanf("%d",&n);
// inițializare elemente vector
for(i=0;i<n;i++)
{
printf("a[%d]=",i);
scanf("%d",&a[i]);
s=s+a[i]; //suma elemente vector
}

151
Algoritmică şi programare

printf("Suma elementelor vectorului=%d\n",s);


system("pause");
return 0;}
Suma elementelor cu valoare para, produsul elementelor cu valoare
impara ale unui vector
#include <stdio.h>
#include <conio.h>
int main()
{
int v[100]; // Declararea vectorului v
int s,p,i,n; // s-suma, p-produsul, i-index in vector, n-număr de
componente
printf(" Introduceți numărul de componente ale vectorului ");
scanf("%d",&n);
printf(" Tastați componentele vectorului \n");
for(s=0,p=1,i=0;i<n;i++)
{
printf(" v[%d] = ",i);
scanf("%d",&v[i]);
if(v[i]%2) // Elementul v[i] impar
p*=v[i];
else // Elementul v[i] par
s+=v[i];
}
printf(" Suma componentelor pare este %d \n",s);
printf(" Produsul componentelor impare este %d \n",p);
getch();
return 0;
}
Sortarea elementelor unui vector in ordine crescătoare
#include <stdio.h>
#include <conio.h>
int main()
{
int i,j,n,pmin;
float v[100], aux;
printf("Introduceți numărul de termeni:");
scanf("%d",&n);
printf("Introduceți componentele:\n");
for(i=0; i<n; i++)

152
Algoritmică şi programare

{
printf("v[%d]=",i);
scanf("%f",&v[i]);
}
for(i=0;i<n;i++)
for(j=i+1;j<n;j++)
if(v[i]>v[j]) //sortare in ordine crescătoare
{
aux=v[i];
v[i]=v[j];
v[j]=aux;
}
printf("Vectorul sortat crescător este:\n");
for(i=0;i<n;i++)
printf("a[%d] = %.2f\n",i,v[i]);
getch();
//system("pause");
return 0;
}
Determinarea valorii minime și poziția sa într-un vector de numere
reale
#include <stdio.h>
#include <conio.h>
int main()
{
int i,n,pmin;
float v[100];
printf("Introduceți numărul de termeni:");
scanf("%d",&n);
printf("Introduceți componentele:\n");
for(i=0; i<n; i++) {
printf("v[%d]=", i);
scanf("%f",&v[i]);
}
for(pmin=0,i=1; i<n; i++)
if(v[pmin]>v[i])
pmin = i;
printf("Minimul este v[%d]=%f.2\n", pmin, v[pmin]);
getch();
}

153
Algoritmică şi programare

Produsul scalar a doi vectori


#include <stdio.h>
#include <conio.h>
int main()
{
int i,j,n;
float v1[100], v2[100],ps=0;
printf("Introduceți numărul de termeni ai vectorilor:");
scanf("%d",&n);
printf("Introduceți componentele vectorului 1:\n");
for(i=0; i<n; i++)
{
printf("v1[%d]=",i);
scanf("%f",&v1[i]);
}
printf("Introduceți componentele vectorului 2:\n");
for(i=0; i<n; i++)
{
printf("v2[%d]=",i);
scanf("%f",&v2[i]);
}
for(i=0;i<n;i++)
ps+=v1[i]*v2[i];
printf("Produsul scalar al vectorului =%.2f\n",ps);

getch();
//system("pause");
return 0;
}

sau

#include <stdio.h>
#include <conio.h>
int main()
{
int i,j,n;
float v1[100], v2[100],ps=0;
printf("Introduceți numărul de termeni ai vectorilor:");
scanf("%d",&n);
printf("Introduceți componentele vectorilor:\n");

154
Algoritmică şi programare

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


{
printf("v1[%d]=",i);
scanf("%f",&v1[i]);
printf("v2[%d]=",i);
scanf("%f",&v2[i]);
ps+=v1[i]*v2[i];
}
printf("Produsul scalar al vectorului =%.2f\n",ps);

getch();
//system("pause");
return 0;
}
Masive de date bidimensionale
Un masiv bidimensional se numește matrice si se declară conform
sintaxei:
tip identificator [m][n];

tip este tipul (predefinit sau utilizator) comun al tuturor elementelor


matricei;
identificator este numele matricei;
m, n este un număr natural nenul care precizează numărul de linii si
coloane ale matricei
Liniile se numerotează cu 0,1, ...,(m-1). Coloanele se numerotează cu
0,1, ...,(n-1). Fiecare element din matrice are același tip, dat de tipul
matricei. Nu se pot defini matrice cu elemente de tipuri diferite. Referirea la
elementul de pe linia i și coloana j din matrice se face cu identificator[i][j].
Declarația:
int matrice[10][10];
• creează o matrice pătratică cu 10 linii si 10 coloane (100
elemente), toate de tip int.
• se rezervă o zonă contiguă de memorie de dimensiune
m*n*sizeof(int)=100*4=400 octeți.
Aplicații practice
Algoritm pentru citirea/scrierea elementelor unei matrice
for(i=0;i<n;i++) for(i=0;i<n;i++)
for(j=0;j<n;j++) for(j=0;j<n;j++)
{ {

155
Algoritmică şi programare

printf("a[%d][%d]=",i,j); printf("%d=", a[i][j]);


scanf("%d",&a[i][j]); }
}

Într-o matrice pătratică, numărul de linii= numărul de coloane (n=m).


Diagonala principala:
• elementele a[i][i], cu i=1,n sau a[i][i], cu i=0,n-1
Diagonala secundara:
• elementele a[i][n-i+1], cu i=1,n sau a[i][n-i-1], cu i=0,n-1
Zonele determinate de diagonale:
• Pe diagonala principala i=j
o Sub diagonala principala: i>j
o Deasupra diagonalei principale: i<j
• Pe diagonala secundară j=n-i+1
o Sub diagonala secundara: j>n-i+1
o Deasupra diagonalei secundare: j<n-i+1
Suma elementelor matricei situate pe diagonala principală si
secundară
#include <stdio.h>
#include<conio.h>
int sp=0,ss=0,i,j,n,a[30][30];
main()
{
printf("Numărul de linii si coloane ale matricei:");
scanf("%d",&n);
for(i=0;i<n;i++)
for(j=0;j<n;j++)
{
printf("a[%d][%d]=",i,j);
scanf("%d",&a[i][j]);
}
for(i=0;i<n;i++)
for(j=0;j<n;j++)
{
if(i==j)
sp=sp+a[i][j];
}
printf("Suma elementelor de pe diagonala principală=%d\n",sp);
for(i=0;i<n;i++)
for(j=0;j<n;j++)
{

156
Algoritmică şi programare

if(i+j==n-1)
ss=ss+a[i][j];
}
printf("Suma elementelor de pe diagonala secundară=%d\n",ss);
//system("pause");
return 0;
}
Suma elemente matrice pe coloana k
#include "stdio.h"
int sum(int a[][20], int n,int k)
{
int i,j,s;
s=0;
for(i=0;i<n;i++)
for(j=0;j<n;j++)
if(j==k) s+=a[i][j];
return s;
}
int main()
{
int n,i,j,a[20][20],k;
printf(" Dimensiune matrice –nr. linii/coloane: ");
scanf("%d",&n);
printf(" Elementele matricei \n");
for(i=0;i<n;i++)
for(j=0;j<n;j++)
{
printf(" a [ %d ][ %d ] = ",i,j);
scanf("%d",&a[i][j]);
}
printf(" Coloana pe care se calculează suma: ");
scanf("%d",&k);
if(k>=0 && k<=n-1)
printf(" Suma elementelor de pe coloana %d este %d\n",k,sum(a,n,k));
else
printf(" Coloana %d inexistenta \n",k);
getch();
}

157
Algoritmică şi programare

Suma elemente matrice de pe linia k


#include "stdio.h"
#include "conio.h"
int sum(int a[][20], int n,int k)
{
int i,j,s;
s=0;
for(i=0;i<n;i++)
for(j=0;j<n;j++)
if(i==k) s+=a[i][j];
return s;
}
int main()
{
int n,i,j,a[20][20],k;
printf(" Dimensiune matrice: ");
scanf("%d",&n);
printf(" Elementele matricei \n");
for(i=0;i<n;i++)
for(j=0;j<n;j++)
{
printf(" a [ %d ][ %d ] = ",i,j);
scanf("%d",&a[i][j]);
}
printf(" Linia pe care se calculează suma: ");
scanf("%d",&k);
if(k>=0 && k<=n-1)
printf(" Suma elementelor de pe linia %d este %d\n",k,sum(a,n,k));
else
printf(" Linia %d inexistenta \n",k);
getch();
}
Masive de date n-dimensionale
Un masiv n-dimensional este limitat doar de memoria fizică a
calculatorului pe care rulează programul si se declară conform sintaxei:
tip identificator [dim1][dim2]…[dimn];
tip este tipul (predefinit sau utilizator) comun al tuturor elementelor
tabloului;
identificator este numele tabloului;

158
Algoritmică şi programare

dim1, dim2, dimn sunt expresii constante a căror valori reprezintă


numărul de componente pentru fiecare dimensiune.
Numărul total de elemente ale unui masiv cu n dimensiuni este egal cu
dim1*dim2*...*dimn.
Pentru memorarea unui masiv cu tipul tip cu n dimensiuni sunt
necesari sizeof(tip)*dim1*dim2*...*dimn octeți.
Accesarea unui element al masivului se face cu construcția
nume[i1][i2]…[in], unde i1, i2, …, in sunt coordonatele elementului pe
fiecare dintre dimensiuni (0 ≤ ij < dimj, 1≤j≤n).
Declarația:
int masiv[3][3][3];

• creează masivul tridimensional cu 3x3x3 elemente de tip real,


toate de tip int.
• se rezervă o zonă contiguă de memorie de dimensiune
sizeof(int)*27=4*27=108 octeți.

159
Algoritmică şi programare

Funcții ale limbajului de programare C/C++


Funcția reprezintă unitatea fundamentală de execuție a programelor
C/C++ prin care se obține o divizare logică (modularizare) a programelor
mari și complexe.
Funcțiile, numite si subrutine, cuprind o serie de instrucțiuni care
efectuează un calcul, realizează o acțiune, implementează un algoritm, etc,
îndeplinind totodată rolul de variabila globală a programului.
Funcțiile împart sarcinile complexe în porțiuni mici de program cu
scopul de a fi mai ușor de înțeles și de programat. Sunt utile întrucât ascund
detaliile de funcționare ale anumitor porțiuni ale programului, îmbunătățind
concomitent modul de lucru al acestuia. Utilizarea funcțiilor împiedică
scrierea repetata a aceluiași cod în procesul de dezvoltare a unui program.
În procesul de concepere a funcțiilor trebuie să ținem cont de
următoarele principii: claritate, lizibilitate, ușurință în întreținere/depanare și
de reutilizare/reapelare a lor. Împărțirea programelor în funcții este arbitrară
și depinde de modul de gândire a programatorului. Funcțiile pot fi refolosite
de cate ori este nevoie, fără sa fie rescrise de la zero.
O funcție în Limbajul C/C++ realizează o anumită acțiune și constituie
o construcție independentă. Funcțiile conțin declarații și instrucțiuni,
încapsulând prelucrări precizate corespunzător și care pot fi refolosite în
diverse programe. Practic, aproape orice program apelează atât funcții din
bibliotecile existente cât și funcții definite în cadrul programului respectiv.
Cu alte cuvinte, programul (aplicația) este de fapt o colecție de funcții
(subprograme).
În utilizarea unei funcții sunt implicate 2 entități: apelantul (care
apelează/cheamă funcția) și funcția apelată.
Modul de funcționare al unei funcții: Apelantul (reprezentând tot o
funcție) transmite parametri (argumente) către funcția apelată. Funcția
apelată primește parametrii transmiși de către apelant, efectuează operațiile
din corpul funcției și returnează rezultatul /rezultatele înapoi funcției
apelante.
Pentru a construi și utiliza o funcție trebuie să cunoaștem trei elemente
care sunt implicate în utilizarea funcțiilor: declararea, apelul și prototipul
(declarație forward) funcției.

1. Declararea funcției 1. Declararea funcției


tip nume_funcţie (lista parametri) float suma_numere (float
{ a, float b)
declarații locale {
instrucțiuni float sum;

160
Algoritmică şi programare

return expresie suma = a + b;


} return suma;
2. Apelul funcției }
nume_functie(p1, p2, . . . ,pn); 2. Apelul funcției
3. Prototipul funcției suma_numere(a,b);
tip nume_funcţie(lista declarații 3. Prototipul funcției
parametri); float suma_numere (float
a, float b); sau
float suma_numere (float,
float);
Avantajele utilizării funcțiilor sunt multiple:
• Evită duplicarea codului sursă ținând cont de faptul că este
greu să menții și să sincronizezi toate duplicările de cod;
• Utilizarea funcțiilor permite dezvoltarea modulară a unui
program de dimensiuni considerabile, sau progresivă, fie de jos
în sus (bottom up), fie de sus în jos (top down), fie combinat.
De aceea, un program complex poate fi mai ușor de scris,
înțeles și depanat dacă este dezvoltat modular;
• Funcțiile pot fi reutilizate de mai multe ori in cadrul unei
aplicații sau de către alte aplicații dacă sunt incluse într-o
bibliotecă de funcții, conducând la diminuarea efortului de
programare în cazul dezvoltării unei noi aplicații;
• Funcțiile pot fi scrise și testate separat de orice aplicație,
conducând la îmbunătățirea timpului de dezvoltare al unei
aplicații ample întrucât erorile apar între module sau funcții);
• Depanarea unei aplicații este sistematizată, deoarece
intervențiile se fac numai în cadrul anumitor module sau
funcții;
Declarații și definiții de funcții
În limbajul C/C++ orice identificator trebuie declarat înainte de a fi
folosit. Funcțiile, la rândul lor, trebuie să respecte si ele această regulă.
Limbajul C/C++ utilizează două tipuri de funcții: funcții void și funcții care
întorc o valoare.
O declarație de funcție înfățișează compilatorul despre numele
funcției, tipul de data al valorii returnate (poate fi și void) și tipurile datelor
folosite în lista de parametri.
Pentru a satisface cerința enunțată anterior, prototipul unei funcții se
plasează chiar înaintea definiției funcției main, la începutul programului.
Prototipul unei funcții este întâlnit în unele limbaje sub denumirea de
declarație forward. Prototipul nu este însoțit de corpul funcției, lista de

161
Algoritmică şi programare

parametri formali este opțională iar declarația prototipului unei funcții se


încheie cu ;
Aplicații practice
Suma a două numere utilizând o funcție
#include "stdio.h"
//----- funcția suma ----
float suma_numere (float a, float b)
{
float sum;
sum=a+b;
return sum;
}
//----- funcția main ----
int main()
{
float a,b, sum;
printf("Inserați valoarea variabilei a:");
scanf("%f",&a);
printf("Inserați valoarea variabilei b:");
scanf("%f",&b);
sum=suma_numere(a,b);
printf("Suma numerelor=%.2f\n", sum);
//system ("PAUSE");
return 0;
}

sau

#include "stdio.h"
//----- funcția suma ----
float suma_numere (float a, float b)
{
float sum;
sum=a+b;
return sum;
}
//----- funcția main ----
int main()
{
float a,b;
printf("Inserați valoarea variabilei a:");

162
Algoritmică şi programare

scanf("%f",&a);
printf("Inserați valoarea variabilei b:");
scanf("%f",&b);
printf("Suma numerelor=%.2f\n", suma_numere(a,b));
//system ("PAUSE");
return 0;
}

sau

#include "stdio.h"
float suma_numere (float, float); //----- prototipul funcției suma ----
//----- funcția main ----
int main()
{
float a,b;
printf("Inserați valoarea variabilei a:");
scanf("%f",&a);
printf("Inserați valoarea variabilei b:");
scanf("%f",&b);
printf("Suma numerelor=%.2f\n", suma_numere(a,b));
//system ("PAUSE");
return 0;
}
//----- funcția suma ----
float suma_numere (float a, float b)
{
float sum;
sum=a+b;
return sum;
}
Suma Gauss și factorialul
#include "stdio.h"
#include<windows.h>
//--------funcția factorial------
int factorial(int a)
{
int i,fact=1;
for (i=1;i<=a;i++)
fact*=i;
return fact;

163
Algoritmică şi programare

}
//--------funcția Gauss------
int Gauss(int a)
{
int i,gauss=0;
for (i=1;i<=a;i++)
gauss+=i;
return gauss;
}
//--------funcția main------
int main(){
int n;
printf("Inserați valoarea variabilei n:");
scanf("%d",&n);
printf("n!=%d\n",factorial(n));
printf("Suma Gauss=%d\n",Gauss(n));
//system ("PAUSE");
return 0;
}

sau

#include "stdio.h"
#include<windows.h>

int factorial(int a); //--------prototipul funcției factorial------


int Gauss(int a); //--------prototipul funcției Gauss------
//----------------------------------
int main()
{
int n;
printf("Inserați valoarea variabilei n:");
scanf("%d",&n);
printf("n!=%d\n",factorial(n));
printf("Suma Gauss=%d\n",Gauss(n));
//system ("PAUSE");
return 0;
}
//--------funcția factorial------
int factorial(int a)
{
int i,fact=1;

164
Algoritmică şi programare

for (i=1;i<=a;i++)
fact*=i;
return fact;
}
//--------funcția Gauss------
int Gauss(int a)
{
int i,gauss=0;
for (i=1;i<=a;i++)
gauss+=i;
return gauss;
}
Aranjamente si combinări
#include "stdio.h"
#include<windows.h>
//--------functia factorial------
int factorial(int a)
{
int i,fact=1;
for (i=1;i<=a;i++)
fact*=i;
return fact;
}
//--------funcția main ----------
int main()
{
int n,k,a,c;
printf("Inserați valoarea variabilei n:");
scanf("%d",&n);
printf("Inserați valoarea variabilei k:");
scanf("%d",&k);
printf("n!=%d\n",factorial(n));
printf("k!=%d\n",factorial(k));
printf("(n-k)!=%d\n",factorial(n-k));
a=factorial(n)/factorial(n-k);
c=factorial(n)/(factorial(k)*factorial(n-k));
printf("Aranjamente=%d, Combinări=%d\n", a, c);
//system ("PAUSE");
return 0;
}

165
Algoritmică şi programare

Iterația si recursivitatea
Ambele tehnici se bazează pe câte o structură de control. Iterația
utilizează o structură repetitivă, iar recursivitatea o structură de selecție.
Recursivitatea implementează repetiția prin apelurile repetate ale aceleiași
funcții. Orice algoritm recursiv se poate rezolva si iterativ.
Iterația reprezintă execuția periodică (ciclică) a unei porțiuni de
program, până la satisfacerea condiției (pentru instrucțiunile repetitive
while, do-while, for). Este structurata sub forma unor funcții care apelează
alte funcții într-o manieră ierarhică.
Recursivitatea este un mecanism care dă posibilitatea unui
subprogram sa se auto-apeleze. Se spune că un obiect sau un fenomen este
definit în mod recursiv dacă în definiția sa există cel puțin o referire la el
însuși. O funcție recursiva este o funcție care se apelează pe ea însăși.
Limbajul C/C++ permite recursivitatea, adică admite ca o funcție să se auto-
apeleze fie direct, fie indirect prin intermediul altei funcții.
La fiecare apel recursiv se alocă pe stivă într-o zonă distinctă față de
apelul precedent sau următor, următoarele informații:
• valorile parametrilor;
• variabilele locale funcției;
• adresa de revenire.

Pe scurt, recursivitatea:
• reprezintă execuția repetată a unui modul;
• verifică o condiție în cursul execuției modulului;
• nesatisfacerea condiției implică repetarea execuției modulului de
la început, chiar dacă execuția curentă nu s-a incheiat (terminat);
• în momentul satisfacerii condiției se revine în ordine inversă în
lanțul de apeluri, reluându-se și încheindu-se apelurile
suspendate.

Recursivitatea este o tehnică de programare frecvent utilizată în


implementarea funcțiilor. Tehnica poate fi folosită în cazul problemelor cu
natură recursivă și simplifică scrierea programelor prin scrierea directă a
formulelor recursive.

Funcțiile C/C++:
• Iterative
• Recursive
o Direct recursive
o Indirect recursive

166
Algoritmică şi programare

Funcția direct recursiva este o funcție P care conține o referință la ea


însăși. Exemplu: P=M(Si,P), unde M este mulțimea ce conține instrucțiunile
Si pe P însuși.
Se deosebesc două tipuri de funcții recursive directe:
• Funcții monoapel, cu un singur apel recursiv. Acestea se pot
scrise rapid sub formă iterativă (nerecursivă).
• Funcții multiapel, cu mai mult de un apel recursiv. Forma
iterativă este greoaie, folosește o stivă pentru memorarea
rezultatelor intermediare.

In recursivitatea indirectă există două procese care se apelează


reciproc astfel: funcția A apelează funcția B iar funcția B apelează funcția
A, ceea ce înseamnă că subprogramele sunt indirect recursive.
În această situație este obligatoriu să declarăm funcțiile prin prototipul
lor.
Exemplu:
Fie șirurile de numere reale AN și BN care au primii termeni AN(0) si
BN(0) egali cu doua valori a și b de același semn. Celelalte elemente ale
șirurilor se determina astfel:
AN(n)=(AN(n-1)+BN(n))/2; BN(n)=sqrt(AN(n)+BN(n-1)).

O funcție recursivă se numește direct recursivă, dacă în definiția ei


există cel puțin un autoapel al ei.
O funcție recursivă se numește indirect recursivă dacă se auto-
apelează prin intermediul unei alte funcții, care la rândul ei se auto-apelează
prin intermediul primei funcții.
În orice funcție recursivă trebuie să existe cel puțin o instrucțiune if
prin care este verificată condiția de terminare a procesului recursiv, fiind
astfel evitată o execuție infinită.
În cazul recursivității indirecte, funcțiile care se auto-apelează indirect
trebuie făcute cunoscute compilatorului prin prototipurile lor, scrise înaintea
funcției main, respectiv, definirii lor.
Utilitatea recursivității ne asigură posibilitatea de a preciza un set
infinit de obiecte printr-un set finit de relații (una sau mai multe).
De exemplu, funcția factorial prezentată anterior, poate fi
implementată recursiv în limbajul C/C++ astfel:
int factorial (int n)
{
if(n == 0)
return 1;
else

167
Algoritmică şi programare

return n * factorial(n - 1);


}

Observație: tipul funcției factorial devine long pentru n>15. Orice


funcție recursivă trebuie să conțină (cel puțin) o instrucțiune if (de obicei la
începutul funcției), prin care se verifică necesitatea unui apel recursiv sau se
pîrîsește funcția! Absența instrucțiunii decizionale IF conduce la un ciclu
fără condiție de terminare (buclă infinită).
Pentru funcțiile diferite de tipul void, apelul recursiv se face cu
ajutorul instrucțiunii return, prin preluarea rezultatului apelului anterior.
Funcțiile implementate recursiv nu conțin instrucțiuni repetitive explicite,
repetarea operațiilor fiind obținută prin apel recursiv.
Algoritmii recursivi sunt folosiți pentru rezolvarea problemelor care
implementează calcule recursive și pentru prelucrarea structurilor de date
definite recursiv (liste, arbori).
Iterația este preferată (uneori) datorită vitezei mai mari de execuție și
folosirii unei cantități mici de memorie utilizată în executarea lor.
Aplicații practice
Factorial iterativ si recursiv
#include<stdio.h>
int fact_r1 (int n) //factorialul recursiv 1
{
if (n == 1 || n == 0 ) return 1;
return n * fact_r1(n - 1);
}
int fact_r2 (int n)
{
return (n >= 1) ? n * fact_r2(n - 1) : 1; //factorialul recursiv 2
}
int fact_it (int n) //Varianta, iterativă
{
int i, f;
for( i = f = 1; i <= n; i ++ )
f * = i;
return f;
}
int main()
{
int n;
printf("Tastati n: ");
scanf("%d",&n);

168
Algoritmică şi programare

printf("n! recursiv1= %d\nn! recursiv2= %d\nn! iterativ= %d\n",


fact_r1(n),fact_r2(n),fact_it(n));
fflush(stdin);
return 0;
}
sau

#include<stdio.h>
//------------declarații forward-----------------
int fact_r1 (int ); //factorialul recursiv 1
int fact_r2 (int ); //factorialul recursiv 2
int fact_it (int ); //Varianta, iterativă
//------------funcția main-----------------
int main()
{
int n;
printf("Tastati n: ");
scanf("%d",&n);
printf("n! recursiv1= %d\nn! recursiv2= %d\nn! iterativ= %d\n",
fact_r1(n),fact_r2(n),fact_it(n));
fflush(stdin);
//getchar();
return 0;
}
//------------funcții factorial-----------------
int fact_r1 (int n) //factorialul recursiv 1
{
if (n == 1 || n == 0 ) return 1;
return n*fact_r1(n-1);
}
int fact_r2 (int n) //factorialul recursiv 2
{
return (n >= 1) ? n * fact_r2(n - 1) : 1;
}
int fact_it (int n) //Varianta, iterativă
{
int i, f;
for( i = f = 1; i <= n; i ++ )
f * = i; // sau f=f*i
return f;
}

169
Algoritmică şi programare

CMMDC recursiv
#include "stdio.h"
#include "conio.h"
int cmmdc(int m, int n)
{
if(n==0) return m;
else return cmmdc(n,m%n);
}
int main()
{
int m,n;
printf("Tastati m = ");
scanf("%d",&m);
printf("Tastati n = ");
scanf("%d",&n);
if(cmmdc(m,n)==1)
printf(" Numerele tastate, %d si %d sunt prime intre ele\n",m,n);
else
printf(" Numerele tastate, %d si %d nu sunt prime intre ele \n",m,n);
printf(" CMMDC( %d , %d ) = %d\n",m,n,cmmdc(m,n));
getch();
}
CMMDC n numere
#include <stdio.h>
#include <conio.h>
// declaratii forward functii CMMDC
unsigned int cmmdc_2(unsigned int , unsigned int );
unsigned int cmmdc_n(unsigned int x[], int n);
int main()
{
unsigned int x[20];
int n,i;
printf("Tastati n: ");
scanf("%d",&n);
for(i = 0; i < n; i ++)
{
printf("Inserati elementul %d= ",i+1);
scanf("%u",&x[i]);
}
if (n==1)
printf("\n Cmmdc-ul numerelor este: %u",x[0]);

170
Algoritmică şi programare

else
printf("\nCmmdc-ul numerelor este: %u",cmmdc_n(x,n));

getch();
}
unsigned int cmmdc_2(unsigned int a, unsigned int b)
{
if(a==b) return a;
if(a>b) return cmmdc_2(a-b,b);
else
return cmmdc_2(a,b-a);
}
unsigned int cmmdc_n(unsigned int x[], int n)
{
if (n==2) return cmmdc_2(x[0],x[1]);
else return cmmdc_2(cmmdc_n(x,n-1),x[n-1]);
}
Problema turnurilor din Hanoi
#include <stdio.h>
#include <conio.h>
void hanoi(int n, char a, char b, char c)
{
if (n==1) {
printf("Se muta discul %d de pe %c pe %c\n", n, a, b);
}
else {
hanoi(n-1,a,c,b);
printf("Se muta discul %d de pe %c pe %c\n", n, a, b);
hanoi(n-1,c,b,a);
}
}
int main()
{
int n;
printf("Problema turnurilor din Hanoi\n");
printf("Tastati numarul de discuri: ");
scanf("%d",&n);
hanoi(n,'A','B','C');
getch();
}

171
Algoritmică şi programare

Suma Gauss calculată recursiv și iterativ


#include<stdio.h>
//fara declaratii forward
int suma_r1(int n) //Gauss recursiv 1
{
if (n==0) return 0;
else return (n + suma_r1(n-1));
}
int suma_r2 (int n) //Gauss recursiv 2
{
return (n > 0) ? n + suma_r2(n - 1) : 0;
}
int suma_it (int n) //Varianta, iterativa
{
int i, s=0;
for( i=1; i<=n; i++ )
s+= i;
return s;
}
int main()
{
int n;
printf("Introduceti n: ");
scanf("%d", &n);
printf("Suma Gauss calculata recursiv1= %d\n",suma_r1(n));
printf("Suma Gauss calculata recursiv2= %d\n",suma_r2(n));
printf("Suma Gauss calculata iterativ = %d\n",suma_it(n));
getchar();
}

sau

#include<stdio.h>
int suma_r1(int n); //Gauss recursiv 1
int suma_r2 (int n); //Gauss recursiv 2
int suma_it (int n); //Varianta, iterativa
int main()
{
int n;
printf("Introduceti n: ");
scanf("%d", &n);
printf("Suma Gauss calculata recursiv1= %d\n",suma_r1(n));

172
Algoritmică şi programare

printf("Suma Gauss calculata recursiv2= %d\n",suma_r2(n));


printf("Suma Gauss calculata iterativ = %d\n",suma_it(n));
getchar();
}
int suma_r1(int n) //Gauss recursiv 1
{
if (n==0) return 0;
else return (n + suma_r1(n-1));
}
int suma_r2 (int n) //Gauss recursiv 2
{
return (n > 0) ? n + suma_r2(n - 1) : 0;
}
int suma_it (int n) //Varianta, iterativa
{
int i, s=0;
for( i=1; i<=n; i++ )
s+= i;
return s;
}
Suma elementelor unui vector - calcul recursiv
#include <stdio.h>
#include <conio.h>
int suma (int n, int a[10]);
int main()
{
int a[10], n;
// Citire date de intrare
printf("Introduceti nr de elemente: ");
scanf("%d", &n);
for (int i=1; i<=n; i++)
{
printf("Elementul [%d] = ", i);
scanf("%d", &a[i]);
}
// Afisarea rezultatelor
printf("Suma = %d", suma(n,a));
getch();
}
int Suma (int n, int a[10])
{

173
Algoritmică şi programare

return (n >0) ? a[n]+suma(n-1,a) : 0;


}
Fibonacci (1)
#include<stdio.h>
int fibo_it (int n)
{
int f1=1,f2=1,fn=1, i;
if (n==0 || n==1) return 1;
for (i=2; i<=n; i++)
{
fn=f1+f2;
f2=f1;
f1=fn;
}
return fn;
}
int fibo_r (int n)
{
if ( n==0 || n==1 ) return 1;
return fibo_r (n-2) + fibo_r (n-1);
}
int main ()
{
int n;
printf("Inserati n:");
scanf ("%d", &n);
printf ("Calcul iterativ\n%d \n", fibo_it(n));
printf ("Calcul recursiv\n%d \n", fibo_r(n));
fflush (stdin);
getchar ();
return 0;
}
Fibonacci (2)
#include<stdio.h>
int fibo_it (int);
int fibo_r (int);
int main (){
int n;
printf("Inserati n:");
scanf ("%d", &n);
printf ("Calcul iterativ\n%d \n", fibo_it(n));

174
Algoritmică şi programare

printf ("Calcul recursiv\n%d \n", fibo_r(n));


fflush (stdin);
getchar ();
return 0;
}
int fibo_it (int n)
{
int f1=1,f2=1,fn=1, i;
if (n==0 || n==1) return 1;
for (i=2; i<=n; i++)
{
fn=f1+f2;
f2=f1;
f1=fn;
}
return fn;
}
int fibo_r (int n)
{
if ( n==0 || n==1 ) return 1;
return fibo_r (n-2) + fibo_r (n-1);
}
Suma cifrelor unui număr natural
#include<stdio.h>
#include<conio.h>
int suma(int ); //prototipul functiei
int main()
{
int n;
printf("Introduceti numarul: ");
scanf("%d", &n);
printf("Suma cifrelor numarului este: %d", suma(n));
getch();
}
int suma(int n)
{
if(!n) return 0; //!n=daca nu exista n
else return n%10+suma(n/10);
}

175
Algoritmică şi programare

Conversia unui număr din baza 10 în baza k


#include<stdio.h>
#include<conio.h>
void schimbbaza(int, int); //prototipul functiei
int main()
{
int n,b;
printf("Tastati numarul:"); scanf("%d",&n);
printf("Tastati baza:"); scanf("%d",&b);
schimbbaza (n,b);
getch();
}
void schimbbaza (int n,int b)
{
int rest=n%b;
if (n>=b) schimbbaza (n/b,b);
printf("%d",rest);
}
Vector. Funcții recursive pentru:
a. citirea componentelor șirului
b. afișarea elementelor din sir
c. suma componentelor
d. produsul componentelor
e. numărul componentelor negative
f. produsul componentelor pozitive
g. media aritmetica a elementelor

#include<stdio.h>
#include<conio.h>
typedef int vector[20];
void citire(vector x,int n) ;
void afisare(vector x,int n) ;
int suma(vector x,int n);
int produs(vector x,int n);
int numar_negative(vector x,int n) ;
int produs_pozitive(vector x,int n) ;
float media(vector x, int m, int n) ;
int main()
{
vector x;
int n; // numar componente

176
Algoritmică şi programare

printf("Tastati numarul de elemente: ");


scanf("%d",&n);
printf("Tastati elementele sirului:\n");
citire(x,n);
printf("Vectorul are in componenta elementele: ");
afisare(x,n);
printf("\nSuma elementelor: %d",suma(x,n-1));
printf("\nProdusul elementelor: %d",produs(x,n-1));
printf("\nNumarul elementelor negative: %d",numar_negative(x,n-
1));
printf("\nProdusul elementelor pozitive: %d",produs_pozitive(x,n-
1));
printf("\nMedia componentelor din sir: %.2f",media(x,n-1,n));
getch();
}
void citire(vector x,int n) //n este dimensiunea sirului
{
printf("\telementul %d: ",n);
scanf("%d",&x[n-1]);
if(n>=2)
citire(x,n-1); //apelul recursiv
}
void afisare(vector x,int n)
{
printf("%d ",x[n-1]);
if(n>=2)
afisare(x,n-1); //apelul recursiv
}
int suma(vector x,int n) //adunarea cifrelor
{
if(n==-1) return 0;
else return x[n]+suma(x,n-1);
}
int produs(vector x,int n) //produsul cifrelor
{
if(n==-1) return 1;
else return x[n]*produs(x,n-1);
}
int numar_negative(vector x,int n) //numarul elemente negative
{
if(n==0) return (x[n]<0);
else return (x[n]<0)+numar_negative(x,n-1);

177
Algoritmică şi programare

}
int produs_pozitive(vector x,int n) //produsul componentelor pozitive
{
if(n==0) return (x[n]>0?x[n]:1);
else return (x[n]>0?x[n]:1)*produs_pozitive(x,n-1);
}
float media(vector x, int m, int n) //media aritmetica a componentelor
sirului
{
return (float)x[m]/n + ((m!=0)?media(x,m-1,n):0);
}
Definirea și transmiterea parametrilor. Parametri formali și
actuali
Limbajul C/C++ pune la dispoziția utilizatorului diverse funcții
predefinite, numite funcții standard. Utilizatorul poate să definească și să
utilizeze propriile sale funcții, numite funcții utilizator.
Există o funcție standard, principală, funcția main(), apelată de
sistemul de operare la începutul execuției oricărui program.
Funcția main gestionează toate funcțiile utilizator din program.
Funcțiile apelate pot comunica cu funcțiile apelante prin intermediul
parametrilor.
Pentru a fi utilizată într‑un program C/C++, o funcție trebuie întâi
declarată (și ulterior definită).
O funcție se definește specificând tipul și numele ei, tipul și numele
argumentelor sale, precum și corpul funcției (compus din declarații și
instrucțiuni).
Argumentele unei funcții precizate la definirea ei se numesc
parametri formali, în timp ce argumentele precizate la apelul ei se numesc
parametri actuali.
Sintaxa declarației unei funcții este:

tip identificator(lista parametri formali)


{
declarații și instrucțiuni
}

unde:
tip identificator(lista parametri formali) se numește antet al funcției,
iar restul reprezintă corpul funcției. Elementul tip reprezintă tipul valorii
întoarse prin apelul funcției, iar identificator este numele funcției.

178
Algoritmică şi programare

Pentru funcțiile care nu returnează nici o valoare, tipul funcției este


void. Pentru fiecare parametru din lista parametri formali trebuie specificat
tipul și numele. Lista parametrilor formali poate să fie şi vidă, caz în care
poate fi înlocuită cu cuvântul cheie void.
Parametrii funcțiilor se pot transmite prin valoare, prin referință și prin
intermediul variabilelor globale. La transmiterea parametrilor prin valoare,
valorile din funcția apelantă sunt copiate în parametrii actuali ai funcției, și
nu pot fi modificate de către funcția apelată deoarece aceasta lucrează cu
copii ale lor.
În cazul transmiterii parametrilor prin referință (sau prin pointeri)
funcția apelantă furnizează funcției apelate adresa zonei de memorie unde
sunt păstrate datele. Funcția apelată poate modifica aceste date accesând
direct zona respectivă de memorie.
O altă metodă de transfer al parametrilor este transmiterea acestora
prin variabile globale. Această metodă se bazează pe faptul că variabilele
globale sunt accesibile din orice funcție.
În limbajul de programare C/C++ există două posibilități de transfer a
datelor între funcția apelantă și cea apelată: prin parametri și prin variabile
globale. Prin utilizarea variabilelor globale nu se face un transfer propriu-
zis, ci se folosesc în comun anumite zone de memorie partajate între toate
funcțiile din program.
În limbajul C/C++ transferul prin parametri se poate realiza prin
valoare și prin referință.
În cazul transferului prin valoare, valoarea parametrului este copiată,
iar funcția apelată lucrează cu această copie. Deci operațiile efectuate asupra
unui parametru formal transmis prin valoare (adică orice parametru care are
tip fundamental), nu modifică, la ieșirea din funcție, parametrul actual
corespunzător. În plus, transferul valorii este însoțit de eventuale conversii
de tip, realizate pe baza informațiilor despre funcție de care dispune
compilatorul.
În concluzie, parametrii transmiși prin valoare se pot modifica în
corpul funcției, dar după terminarea apelului funcției apelate în funcția
apelantă aceștia au aceleași valori pe care le-au avut înaintea apelului. Din
acest motiv, transferul prin valoare este folosit pentru transmiterea datelor
de intrare.
Folosind transferul prin valoare se pot transmite funcției numai
parametri de intrare.
Exemplu:

#include <stdio.h>
#include <conio.h>
int suma(int ); // prototip functie

179
Algoritmică şi programare

int main()
{
int n;
printf(" Valoarea lui n = ");
scanf("%d",&n);
printf("Suma primelor %d numere naturale este %d\n", n, suma(n));
getch();
}
int suma(int k) // parametrul transmis prin valoare
{
int s = 0,i;
for(i=1;i<=k;i++)
s+=i;
return s;
}

Pentru transmiterea parametrilor de ieșire se folosește transferul prin


referință (adică prin pointeri), caz în care se transmit funcției adrese de
variabile. Antetul unei funcții care folosește transferul de parametri de tip
referință este:
tip identificator(…, tip *variabila,…)

Parametrii transmiși prin referință se pot modifica în corpul funcției,


iar după terminarea apelului funcției aceștia au valorile primite în timpul
execuției funcției apelate.
În acest caz, funcția primește ca parametru adresa zonei de memorie
unde se găsesc datele și poate să le actualizeze pe acestea modificând
conținutul zonei de memorie.
Reluăm exemplul anterior. Acum, funcția suma are un parametru
formal de intrare k și un parametru formal de ieșire s, care este un pointer la
tipul int.

#include <stdio.h>
#include <conio.h>
int suma(int k,int *s);

int main()
{
int n,sum;
printf("Valoarea lui n = ");
scanf("%d",&n);
// Apelul functiei

180
Algoritmică şi programare

suma(n,&sum); // &sum inseamna adresa lui sum


printf("Suma primelor %d numere naturale este %d\n", n, sum);
getch();
}
// Parametrul n se transmite prin valoare
// Parametrul s se transmite prin adresa
int suma(int k,int *s)
{
int sum = 0,i;
for(i = 1; i <= k;i ++)
sum+=i;
*s=sum;

Pentru parametrii de tip masiv de date, transferul prin referință se face


în mod implicit, deoarece numele masivului este pointer către zona de
memorie unde este stocat masivul. Astfel următoarele prototipuri sunt
echivalente:

tip identificator1(int masiv[]);


tip identificator2(int *masiv);

Următorul exemplu prezintă o funcție pentru calculul sumei


componentelor unui vector. Funcția suma are doi parametri de intrare.
Citirea vectorului se realizează cu funcția citvector. Parametrul vector este
transferat prin referință deoarece este un masiv de întregi.

#include <stdio.h>
#include <conio.h>
void citvector(int k,int vector[])
{
int i;
for(i=0;i<k;i++)
{
printf("v [ %d ] = ",i);
scanf("%d",&vector[i]);
}
}
int suma(int k,int vector[])
{
int s = 0,i;
for(i=0; i<k; i++)
s+=vector[i];

181
Algoritmică şi programare

return s;
}
int main()
{
int n,v[100],i;
printf("Numar de componente n = ");
scanf("%d",&n);
citvector(n, v);
printf("Suma componentelor este: %d\n", suma(n,v));
getch();
}

Variabilele globale se declară în afara funcțiilor, inclusiv în afara


funcției main() și pot fi referite din orice alte funcții, inclusiv din funcția
main().
Astfel, transferul de parametri între funcția apelantă și cea apelată se
poate face și prin variabile globale.
Pentru exemplificare, reluăm unul dintre exemplele anterioare.
Parametrii de intrare ai funcției suma sunt declarați ca variabile globale.
Funcția suma are lista parametrilor formali vidă.

#include <stdio.h>
#include <conio.h>
int n, vector[100]; // variabile globale
void citvector()
{
int i;
for(i=0;i<n;i++)
{
printf(" v [ %d ] = ",i);
scanf("%d", &vector[i]);
}
}
int suma()
{
int s = 0,i;
for(i=0;i<n;i++)
s+=vector[i];
return s;
}
int main()
{

182
Algoritmică şi programare

int i;
printf(" Numar de componente n = ");
scanf("%d",&n);
citvector();
printf("Suma componentelor este: %d\n",suma());
getch();
}

Cele două metode de transmitere a parametrilor (prin variabile globale


și prin parametri) pot fi folosite împreună. De exemplu, declarăm numai
vectorul vector variabilă globală. Funcția suma are un singur parametru
formal k.
#include <stdio.h>
#include <conio.h>
int vector[100]; // Declararea variabilei globale vector
void citvector(int k)
{
int i;
for(i=0;i<k;i++)
{
printf(" v [ %d ] = ",i);
scanf("%d", &vector[i]);
}
}
int suma(int k) //parametru format
{
int s = 0,i;
for(i=0;i<k;i++)
s+=vector[i];
return s;
}
int main()
{
int n;
printf(" Numar de componente n = ");
scanf("%d",&n);
citvector(n);
printf("Suma componentelor: %d\n", suma(n));
getch();
}

183
Algoritmică şi programare

Descompunerea Goldbach
#include <stdio.h>
#include <conio.h>
int prim(int);
int main() // Definitia functiei principale
{
int n,i,dif,p;
printf(" n = ");
scanf("%d",&n);
if(n%2==0)
{
i=2;
while(i<n-1)
{
if(prim(i))
{
p=i;
i++;
}
else
i++;
}
dif=n-p;
printf(" %d = %d + %d \n",n,dif,p);
}
else
printf(" %d nu este numar par \n",n);
getch();
}
int prim(int n) // Functia prim testează dacă numarul n este prim
{
int i,p;
p=1;
for(i=2;i<=n/2;i++)
if(n%i==0)
p=0;
return p;
}

184
Algoritmică şi programare

Proprietatea Goldbach pe mulțimea{3,4,...,k}


#include <stdio.h>
#include <conio.h>
int prim(int);
int main() // Definitia functiei principale
{
int k,n,i,dif,p;
printf(" k = "); scanf("%d",&k);
for(n=4;n<=k;n++)
{
if(n%2==0)
{
i=2;
while(i<n-1)
{
if(prim(i))
{
p=i;
i++;
}
else
i++;
}
dif=n-p;
printf(" %d = %d + %d \n",n,dif,p);
}
}
getch();
}
int prim(int n) //n este prim ?
{
int i,p;
p=1;
for(i=2;i<=n/2;i++)
if(n%i==0)
p=0;
return p;
}

185
Algoritmică şi programare

Adrese de memorie. Pointeri


Orice variabilă are două elemente caracteristice:
• Valoarea pe care o are variabila la un moment dat (int
a=10;)
• valoarea adresei locației de memorie (&a).

O zonă de memorie este o succesiune 1, 2, 4, 8 sau mai multe locații


consecutive de memorie (octeți). O variabilă se memorează într o zonă de
memorie de o anumită lungime, funcție de tipul ei: o variabilă de tip char se
memorează pe 1 octet, o variabila de tip int sau float pe 4 octeți iar un
double pe 8 octeți.
• printf("Dimensiunea int=%d\n", sizeof (int));
• printf("Dimensiunea float=%d\n", sizeof (float));
• printf("Dimensiunea char=%d\n", sizeof (char));
• printf("Dimensiunea double=%d\n", sizeof (double));
• printf("Dimensiunea u int=%d", sizeof (unsigned int));

Adresa locației de memorie în care este stocată valoarea unei variabile


se poate obține aplicând operatorul de adresă (operatorul &) înaintea
numelui variabilei (operație întâlnită frecvent în cazul apelării funcției
scanf).
Operatorul de adresă poate fi utilizat pentru obținerea valorii adresei
oricărei variabile.
Exemplu:
int a=2;
printf("Adresa lui a=%d\n",&a);
Operatorul &, numit "de adresa", furnizează adresa zonei de memorie
unde este memorata valoarea variabilei a.
Manipularea datelor se face cu ajutorul:
• identificatorilor (nume de variabile);
• adreselor de memorie ale variabilelor si conținutul zonei
de memorie de la adresa respectiva (pointerilor)

Mecanismul transmiterii parametrilor prin intermediul pointerilor este


o extensie a transmiterii prin referință.
Un pointer este o variabilă care are ca valoare adresa unei zone de
memorie a unui obiect (o variabila sau o funcție). Pointerul este o variabilă
care poate memora numai adrese de memorie.
Fie T = un tip de data standard(int, double, char, etc.)

186
Algoritmică şi programare

Fie T* = un nou tip de data de tip pointer (o variabila de tip pointer are
ca valoare adresele variabilelor de tip pointer).
int *p; //variabila de tip pointer si poate sa memoreze numai adrese de
memorie de variabile ale căror valori sunt de tip întreg
float *f //variabila de tip pointer si poate sa memoreze numai adrese
de memorie de variabile ale căror valori sunt de tip float
T*p //p este o variabila pointer si va conține adrese de memorie
alocate unor date de tip T
// * semnifica faptul ca variabila p este pointer la tipul de data T

Pointerii se utilizează pentru a face referire (a avea acces) la valoarea


unei variabile atunci când se cunoaște adresa ei.
Astfel, un pointer nu reprezintă numai adresa unei variabile ci si:
• adresa unei zone de memorie;
• tipul variabilei (int, char, double etc.) care este memorată
în acea zonă de memorie.

int a=2,*p;
printf("a=%d, Adresa lui a=%d\n",a,&a);
p=&a; //operatorul & furnizează adresa zonei de
memorie unde este memorata valoarea variabilei a
printf("p=%d, Adresa lui a=%d\n",p,&a);
printf("p=%d, Adresa lui a=%d\n",p,p);

În general, un pointer poate fi inițializat ca orice altă variabilă deși, în


mod normal, singurele valori care au sens sunt zero sau o expresie care
implică adresele unor date definite anterior.
Valorile si formatul adreselor de memorie depind de arhitectura
calculatorului si de sistemul de operare sub care rulează. Din motive de
portabilitate, la declararea unei variabile care conține o adresă de memorie
se va utiliza un specificator de format special: %p, pentru tipărirea valorilor
reprezentând adrese de memorie.
Sunt doua mari categorii de pointeri:
• pointeri către variabile
• pointeri către funcții.
Avantajele utilizării pointerilor sunt:
• oferă posibilitatea de a modifica argumentele de apelare
a funcțiilor,
• permite alocarea dinamică a memoriei,
• permit exprimarea unor operații la un nivel mai scăzut,
• conduc la un cod mai compact si mai eficient,

187
Algoritmică şi programare

• îmbunătățesc eficienta anumitor rutine.

Pentru a obține valoarea obiectului indicat de un pointer se utilizează


operatorul de dereferenţiere sau indirectare (operatorul *).

int a=2,*p; /*definesc o variabila de tip întreg si o


variabila p pointer la întreg care va conține
adrese de memorie in care se memorează valori
întregi*/
p=&a; //operatorul & furnizează adresa zonei de memorie
unde este memorata valoarea variabilei a
printf("a=%d, Adresa lui a=%d\n",a,&a);
printf("a=%d, Adresa lui a=%d\n",a,p);
printf("a=%d, Adresa lui a=%d\n",*p,&a);
printf("a=%d, Adresa lui a=%d\n",*p,p); //operatorul unar
*, numit de "dereferenţiere", sau "indirectare"
*p=20; //reinițializare variabila a (a=20)
printf("a=%d, Adresa lui a=%d\n",a,&a);
printf("a=%d, Adresa lui a=%d\n",a,p);
printf("a=%d, Adresa lui a=%d\n",*p,p);

Se pot declara si pointeri generici, de tip void* numit pointerul vid


(tipul datelor indicate de ei nu este cunoscut).
Un pointer de tip void reprezintă doar o adresă de memorie a unui
obiect oarecare:
• dimensiunea zonei de memorie indicate si interpretarea
informației conținute, nu sunt definite;
• poate apare în atribuiri, în ambele sensuri, cu pointeri de
orice alt tip;
• folosind o conversie explicită de tip cu operatorul
cast:(tip *), poate fi convertit la orice alt tip de pointer
(pe același calculator, toate adresele au aceeași lungime)

void*p //se definește un pointer către tipul vid si care


poate sa memoreze adrese de tipuri diferite (float, int, etc.)
float a;
p=&a;
p=*(float*)a; se memorează valoarea adresei variabilei a
de tip real (pentru modificarea valorii conținutului
pointerului)

188
Algoritmică şi programare

Exemplu:

Operatorii de adresare si dereferenţiere


Ca si în cazul variabilelor de orice tip, variabilele pointer declarate si
neinițializate conțin valori aliatoare. Pentru a atribui variabilei p valoarea
adresei variabilei a, se folosește operatorul de adresă într-o expresie de
atribuire de forma:

189
Algoritmică şi programare

p=&a;

În urma acestei operații, se poate spune că pointerul p indică spre


variabila a.
Pentru a obține valoarea obiectului indicat de un pointer se utilizează
operatorul de dereferenţiere sau indirectare (operatorul *). Dacă p este o
variabilă de tip pointer care are ca valoare adresa locației de memorie a
variabilei întregi a (p indică spre a) atunci expresia *p reprezintă o valoare
întreagă (valoarea variabilei a).
int a=5, *p;
p=&a;
*p=6; // variabila a ia valoarea 6
Când se utilizează operatorul de dereferenţiere (indirectare) trebuie
avut grijă ca variabila pointer asupra căreia se aplică să fi fost inițializată,
adică să conțină adresa unei locații de memorie valide. Secvența următoare
este incorectă si poate genera erori la execuție:
greșit corect

int a, *p; int a=5, *p;


p=&a; p=&a;
*p=6; *p=6;
Variabilele pointer pot fi utilizate în expresii si direct, fără indirectare.

int a=5, *p, *q;


p=&a;
q=p; // atribuire de pointeri - se copiază conținutul lui p in
q, rezulta ca q indica spre același obiect ca p

Pe scurt:
• Un nume de variabilă referă direct o valoare;
• Un pointer referă indirect o valoare;
• Referirea unei valori printr-un pointer se numește
indirectare.

Toate operațiile de manipulare a pointerilor iau în considerare în mod


automat dimensiunea obiectului spre care se indică. Pointerii pot fi folosiți
în expresii aritmetice, asignări și comparații, însă nu toți operatorii pot avea
pointeri ca operanzi.
Asupra pointerilor pot fi realizate operații de:
• incrementare (++)

190
Algoritmică şi programare

• decrementare (--)
• adăugare a unui întreg (+ sau +=)
• scădere a unui întreg (- sau -=)
• scădere a unui pointer din alt pointer
Operațiile permise cu pointeri sunt:
• atribuirea pointerilor de același tip;
• adunarea unui pointer cu un întreg sau scăderea unui
întreg;
• scăderea sau compararea a doi pointeri care indică spre
elementele aceluiași tablou;
• atribuirea valorii zero (NULL) sau compararea cu
aceasta.

Un pointer poate fi asignat altui pointer doar dacă cei doi au același
tip. În caz contrar, trebuie aplicată o operație de conversie pentru ca
pointerul din dreapta asignării să fie adus la tipul pointerului din stânga.
Excepție de la această regulă în face pointerul void* care este un tip
generic și poate reprezenta orice tip de pointer fără a mai fi nevoie de cast.
Pe de altă parte, pointerul void* nu poate fi nepreferențiat pentru că numărul
de byți corespunzător lui nu poate fi determinat de compilator
Sunt ilegale următoarele operații aritmetice asupra pointerilor:
• adunarea a doi pointeri;
• înmulțirea sau împărțirea pointerilor;
• deplasarea sau aplicarea unor măști;
• adunarea cu valori de tip float sau double ;
• atribuirea a doi pointeri de tipuri diferite fără a folosi
operatorul cast (cu excepția tipului void *).
Pointeri la funcții
Deoarece funcțiile sunt considerate variabile globale într-un program
scris în limbajul de programare C/C++ are sens noțiunea de pointer la o
funcție. Numele unei funcții este un pointer care indică adresa de memorie
unde începe codul executabil al funcției. Aceasta permite transmiterea
funcțiilor ca parametri în alte funcții, precum și lucrul cu tabele de funcții.
Considerăm dată definiția unei funcții:

tip nume(tip 1 pf 1 , tip 2 pf 2 ,..., tip n pf n )


{
corp funcție
}

191
Algoritmică şi programare

în care: tip este tipul valorii returnată de funcție, nume este identificatorul
(numele) funcției, tip 1 este tipul parametrului formal pf 1 , tip 2 este tipul
parametrului formal pf 2 , ..., tip n este tipul parametrului formal pf n .
Un pointer notat cu p la funcţia astfel definită se declară astfel:

tip (*p)( tip1, tip2,..., tipn);

în care: tip este tipul valorii returnată de funcţie, p este pointerul la funcţie,
tip 1 , tip 2 ,..., tip n sunt tipurile parametrilor formali ai funcţiei.
Iniţializarea pointerului se realizează cu sintaxa:

tip (*p)( tip1, tip2,..., tipn)=nume;

în care nume este numele funcţiei. Unui pointer la o funcţie i se poate atribui
numai numele oricărei funcţii care are prototip identic cu prototipul funcţiei
spre care pointează:

tip numep( tip1, tip2,..., tipn);

în care nume p este numele unei funcţii oarecare cu acelaşi prototip.


Atribuirea unei adrese de funcţie unui pointer la o funcţie se face cu sintaxa

p=nume;

Lansarea în execuţie a funcţiei respective prin intermediul pointerului


p la funcţia respectivă se face cu sintaxa:
(*p)(pa1, pa2, ..., pan);

în care pa 1 , pa 2 , ..., pa n sunt parametrii actuali de apel ai funcţiei.


Aplicații practice
Suma elementelor unui vector fără pointeri
#include <stdio.h>
#include<conio.h>
int s=0,i,n,a[25];
main()
{printf("Stabiliti numarul de elemente ale vectorului, n<=25:");
scanf("%d",&n);
// initializare elemente vector
for(i=0;i<n;i++)
{
printf("a[%d]=",i);

192
Algoritmică şi programare

scanf("%d",&a[i]);
}
// afisare elemente vector
printf("Elementele vectorului sunt:\n");
for(i=0;i<n;i++)
printf("a[%d]=%d\n",i,a[i]);
//suma elemente vector
for(i=0;i<n;i++)
s=s+a[i];
printf("Suma elementelor vectorului=%d\n",s);
//system("pause");
return 0;
}
Suma elementelor unui vector cu pointeri
#include <stdio.h>
#include<conio.h>
int s=0,i,n,a[25],*p;
main()
{printf("Stabiliti numarul de elemente ale vectorului, n<=25:");
scanf("%d",&n);
// initializare elemente vector
for(i=0;i<n;i++)
{
printf("a[%d]=",i);
scanf("%d",&a[i]);
}
// afisare elemente vector
p=&a[0];
printf("Elementele vectorului sunt:\n");
for(i=0;i<n;i++)
printf("a[%d]=%d\n",i,*p+i);
//suma elemente vector
for(i=0;i<n;i++)
s=s+*(p+i);
printf("Suma elementelor vectorului=%d\n",s);
//system("pause");
return 0;
}

193
Algoritmică şi programare

Suma elementelor unui vector utilizând pointeri la o funcție


#include <stdio.h>
#include<conio.h>
int suma_vector(int i, int n, int *p)//suma elemente vector
{
int s=0;
for(i=0;i<n;)
{
s+=(*p+i);
i++;
}
printf("Suma elemente vector=%d\n",s);
return s;
}
//-----------------------
main()
{
int i,n,a[25],*p;
p=&a[0];
printf("Stabiliti numarul de elemente ale vectorului, n<=25:");
scanf("%d",&n);
printf("Inserati elementele vectorului:\n"); // initializare elemente
vector
for(i=0;i<n;i++)
{
printf("a[%d]=",i);
scanf("%d",&a[i]);
}
suma_vector(i,n,a);
//system("pause");
return 0;
}
Test palindrom
#include<stdio.h>
#include<stdlib.h>
#include<conio.h>
int main()
{
int n, i, pal=1;
int *vector;
printf("Dimensiunea vectorului n:");

194
Algoritmică şi programare

scanf("%i", &n);
vector = (int*) malloc(sizeof(int) * n);
for(i=0; i<n; ++i) {
printf("Introduceti v[%d]:", i);
scanf("%i", &vector[i]);
}
for(i=0; i<n/2 && pal == 1; ++i)
if(vector[i] != vector[n-i-1])
pal = 0;
if(pal == 1)
printf("Vectorul este palindrom.\n");
else
printf("Vectorul nu este palindrom.\n");
free(vector);
getch();
}
Implementare operații aritmetice elementare
// Operatiile aritmetice +,-,*,% cu intregi
#include "stdio.h"
#include "conio.h"
int suma(int, int); // prototipul functiei suma
int dif(int, int); // prototipul functiei dif
int prod(int, int); // prototipul functiei prod
int mod(int, int); // prototipul functiei mod
void afis(int, int, int, char); // prototipul functiei afis
// Functie care returneaza suma argumentelor a si b
int suma(int a, int b)
{
return a+b;
}
// Functie care returneaza diferenta argumentelor a si b
int dif(int a ,int b)
{
return a-b;
}
// Functie care returneaza produsul argumentelor a si b
int prod(int a, int b)
{
return a*b;
}
// Functie care returneaza restul impartirii intregi a argumentelor a si b

195
Algoritmică şi programare

// Daca b==0 functia returneaza valoarea -1


int mod(int a, int b)
{
if(b)
return a%b;
else
return -1;
}
// Functie pentru afisarea rezultatului rez al operatiei op intre
argumentele a si b
void afis(int a, int b, int rez, char op)
{
printf(" %d %c %d = %d \n",a,op,b,rez);
}
int main()
{
char op[4]={'+','-','*','%'}; // Lista operatorilor
int i,rez,a,b;
printf(" Inserati a: ");
scanf("%d",&a);
printf(" Inserati b: ");
scanf("%d",&b);
printf("\n Rezultatele operatiilor:\n\n");
rez=suma(a,b); // calcul suma
afis(a,b,rez,op[0]); // afisare rezultat suma
rez=dif(a,b); // calcul diferenta
afis(a,b,rez,op[1]); // afisare rezultat diferenta
rez=prod(a,b); // calcul produs
afis(a,b,rez,op[2]); // afisare rezultat produs
rez=mod(a,b); // calcul modulo
if(rez != -1) // daca b != 0
afis(a,b,rez,op[3]); // afisare rezultat modulo
else
{
printf(" Operatia %d %c %d este nedefinita \n",a,op[3],b);
printf(" Impartire la zero \n");
}
getch();
}

196
Algoritmică şi programare

Implementare operații aritmetice elementare folosind pointeri la funcţii


int main()
{
int (*ps)(int,int)=suma; // ps pointer la functia suma
int (*pd)(int,int)=dif; // pd pointer la functia dif
int (*pp)(int,int)=prod; // pp pointer la functia prod
int (*pm)(int,int)=mod; // pm pointer la functia mod
void (*pa)(int,int,int,char)=afis; // pa pointer la functia afis
char op[4]={'+','-','*','%'}; // lista operatorilor
int i,rez,a,b;
printf(" Inserati a: "); scanf("%d",&a);
printf(" Inserati b: "); scanf("%d",&b);
printf("\n Rezultatele operatiilor:\n\n");
rez=(*ps)(a,b); // calcul suma
(*pa)(a,b,rez,op[0]); // afisare rezultat suma
rez=(*pd)(a,b); // calcul diferenta
(*pa)(a,b,rez,op[1]); // afisare rezultat diferenta
rez=(*pp)(a,b); // calcul produs
(*pa)(a,b,rez,op[2]); // afisare rezultat produs
rez=(*pm)(a,b); // calcul modulo
if(rez != -1) // daca b != 0
(*pa)(a,b,rez,op[3]); // afisare rezultat modulo
else
{
printf(" Operatia %d %c %d este nedefinita \n",a,op[3],b);
printf(" Impartire la zero \n");
}
getch();
}
int main()
{
int (*p)(int,int); // p pointer la o functie cu prototipul int ... (int, int);
// neinitializat
void (*pa)(int,int,int,char)=afis; // pa pointer la o functie cu
prototipul
// void ... (int, int, int, char);
// initializat cu functia afis
char op[4]={'+','-','*','%'}; // lista operatorilor
int i,rez,a,b;
printf(" Inserati a: "); scanf("%d",&a);
printf(" Inserati b: "); scanf("%d",&b);
printf("\n Rezultatele operatiilor:\n\n");

197
Algoritmică şi programare

p=suma; // p pointeaza functia suma


rez=(*p)(a,b); // calcul suma
(*pa)(a,b,rez,op[0]); // afisare rezultat suma
p=dif; // p pointeaza functia dif
rez=(*p)(a,b); // calcul diferenta
(*pa)(a,b,rez,op[1]); // afisare rezultat diferenta
p=prod; // p pointeaza functia prod
rez=(*p)(a,b); // calcul produs
(*pa)(a,b,rez,op[2]); // afisare rezultat produs
p=mod; // p pointeaza functia mod
rez=(*p)(a,b); // calcul modulo
if(rez != -1) // daca b != 0
(*pa)(a,b,rez,op[3]); // afisare rezultat modulo
else
{
printf(" Operatia %d %c %d este nedefinita \n",a,op[3],b);
printf(" Impartire la zero \n");
}
getch();
}

198
Algoritmică şi programare

Structuri și uniuni
Structuri
La nivel conceptual structurile sunt șabloane formate din date de
diferite tipuri numite câmpuri, la care accesul se realizează printr-un nume
simbolic, care se numește selector.
Structura (înregistrare în alte limbaje) este o selecție de variabile de
diferite tipuri. Rezultă că sub un nume comun vom putea grupa variabile de
diferite tipuri. Structura se declara înaintea funcției main().

Sintaxa unei structuri este următoarea:

struct [nume]
{
tip 1 var 1 ;
tip 2 va r2 ;
…………….
tip n var n ;
};

struct: cuvânt cheie care identifică ceea ce urmează ca fiind o definiție


de structură.
nume: parte opțională a definiției de structură.

Fig. 62 Structura

199
Algoritmică şi programare

Aceasta permite declararea unor variabile cu aceeași structură în


diferite segmente de program. Între acolade se poate declara un set de
variabile, având tipuri diferite de date.
Structurile pot fi folosite independent sau în combinație cu alte tipuri
de date.
Se pot enumera:
• structuri simple;
• tablouri de structuri;
• structuri conținând tablouri;
• pointeri la structuri;
• structuri auto-referite (liste).

Exemplu:

#include <stdio.h>
#include <conio.h>
int main()
{
struct student {
char nume[20];
char prenume[20];
int nota;
} student[25], aux;
int i,j,n;
printf("Numar studenti:");
scanf("%d",&n);
for(i=0;i<n;i++) {
printf("Nume si prenume student %d:", i+1);
scanf("%s%s",student[i].nume, student[i].prenume);
printf("Nota studentului %s %s:", student[i].nume,
student[i].prenume);
scanf("%d",&student[i].nota);
}
for(i=0;i<n;i++)
for(j=i+1;j<n;j++)
if(student[i].nota < student[j].nota) {
aux=student[i];
student[i]=student[j];
student[j]=aux;
}
printf("Rezultatele grupei:\n");

200
Algoritmică şi programare

for(i=0;i<n;i++)
printf("%s %s\t%d\n", student[i].nume, student[i].prenume,
student[i].nota);
getch();
}

sau

#include <stdio.h>
#include <stdlib.h>
int main()
{
struct student{
char nume[20];
int mate,fiz,chim;
float med;
}student[30],aux;
int i,j,n;

printf("Numar studenti:");
scanf("%d",&n);
for(i=0;i<n;i++)
{

printf("Numele studentului %d:",i+1);


scanf("%s",&student[i].nume);
printf("Notele studentului %s la disciplina matematica
:",student[i].nume);
scanf("%d",&student[i].mate);
printf("Notele studentului %s la disciplina fizica
:",student[i].nume);
scanf("%d",&student[i].fiz);
printf("Notele studentului %s la disciplina chimie
:",student[i].nume);
scanf("%d",&student[i].chim);

student[i].med=(student[i].mate+student[i].fiz+student[i].chim)/3;
}

for(i=0;i<n;i++)
for(j=i+1;j<n;j++)
if(student[i].med>student[j].med)

201
Algoritmică şi programare

{aux=student[i];
student[i]=student[j];
student[j]=aux;
}

printf("Rezultatele studentilor in ordine descrescatoare : \n");


for(i=0; i<n; i++)
printf("%s are media %.2f\n",student[i].nume,student[i].med);
getch();
}

Tip Variabile Pointeri

Sintaxa struct tag_name struct tag_name


{ {
data type data type var_name1;
var_name1; data type var_name2;
data type data type var_name3;
var_name2; };
data type
var_name3;
};
Ex: struct student struct student
{ {
int nr_matricol; int nr_matricol;
char nume[10]; char nume[10];
float medie; float medie;
}; };
Declarare variabile struct student struct student *raport, rap;
structura raport;

Initializare variabile struct student raport struct student rap =


struct = {100, “Ion”, 9.5}; {100, “Ion”, 9.5};raport = &rap;

Scriere membri struct raport.nr_matricol raport- (*raport).nr_matricol


raport.nume >nr_matricol (*raport).nume
raport.medie raport->nume (*raport).medie
raport->medie

Tabel 10 Structuri
Structurile pot participa la formarea unor tipuri de date, in cazul de
mai jos, un tablou unidimensional care are componentele de tip structuri.

202
Algoritmică şi programare

#define Marime_nume 20
#define Marime_adresa 20
#define Nrpers 20
#include <stdio.h>
main()
{
struct data
{int zi;
int luna;
int an;
};
struct persoana
{char nume [Marime_nume];
char adresa[Marime_adresa];
struct data data_nastere;
char nrtelefon;
}baza_personal[Nrpers];
}
Pointeri către structuri
Exemplu:

# include "stdio.h"
struct Student
{
char nume[20];
char prenume[20];
int varsta;
char sex;
float m_bac;
};
main()
{
struct Student *stud, p; stud=&p;
printf("Inserati Nume:"); gets((*stud).nume);
printf("Inserati Prenume:"); gets((*stud).prenume);

printf("\n\nNumele studentului este:%s\n", (*stud).nume);


printf("Prenumele studentului este:%s\n", (*stud).prenume);
}

203
Algoritmică şi programare

Typedef. Crearea unor noi tipuri de date


Programatorul, cu ajutorul construcției typedef, poate atribui un
anumit nume unui tip de data indiferent de faptul că acesta este un tip
predefinit sau definit propriu de utilizator.
Formatul este următorul:
typedef tip identificator;

tip – poate fi numele tipului predefinit, o declarație de tip structura sau


tablou.
identificator – numele care se atribuie tipului respectiv.
Exemplu:

#include "stdio.h"
main()
{
typedef int intreg;
intreg an_nastere;
an_nastere=1999;
printf("Sunt nascut in anul %d", an_nastere);
}
Typedef de tip Structura
# include "stdio.h"
typedef struct Student
{
char nume[20];
char prenume[20];
int varsta;
char sex;
float m_bac;
}Student;
main()
{
Student stud;
printf("Inserati Nume:"); gets(stud.nume);
printf("Inserati Prenume:"); gets(stud.prenume);
printf("Inserati varsta:"); scanf("%d", &stud.varsta);
fflush(stdin); //curatarea bufferului
printf("Inserati sexul (M/F):"); scanf("%c", &stud.sex);
printf("Inserati media obtinuta la BAC:"); scanf("%f",
&stud.m_bac);

204
Algoritmică şi programare

printf("\n\nNumele studentului este:%s\n", stud.nume);


printf("Prenumele studentului este:%s\n", stud.prenume);
printf("Varsta studentului este:%d\n", stud.varsta);
printf("Sexul studentului este:%c\n", stud.sex);
printf("Media obtinuta la BAC este:%.2f\n", stud.m_bac);
}
Uniuni
Uniunile sunt asemănătoare structurilor, utilizează o singura locație de
memorie pentru a stoca mai mult de o singura variabila, adică o colecție de
date de tipuri și mărimi diferite. O uniune se declară printr‑o construcție
asemănătoare declarației de structură. Fiecare element al uniunii se numește
membru.

union [nume]
{
tip 1 var 1 ;
tip 2 var 2 ;
…………….
tip n var n ;
};

union: cuvânt cheie care identifică ceea ce urmează ca fiind o definiție


de tip union.
nume: parte opțională a definiției uniunii.
Între acolade se poate declara un set de variabile, având tipuri diferite.
Exemplu:
union Student
{ char nume[20];
char prenume[20];
int varsta;
char sex;
float m_bac;
};

Tip Variabile Pointeri


Sintaxa union tag_name union tag_name
{ {
data type data type var_name1;
var_name1; data type var_name2;

205
Algoritmică şi programare

Tip Variabile Pointeri


data type data type var_name3;
var_name2; };
data type
var_name3;
};
Ex: union student union student
{ {
int nr_matricol; int nr_matricol;
char nume[10]; char nume[10];
float medie; float medie;
}; };
Declarare variabile union student union student *raport, rap;
uniune raport;
Initializare variabile union student union student rap = {100,
uniune raport = {100, “Ion”, 9.5}; raport = &rap;
“Ion”, 9.5};
Scriere membri raport.nr_matricol raport->nr_matricol
uniune raport.nume raport->nume
raport.medie raport->medie
Tabel 11 Uniuni
Exemplu:
# include "stdio.h"
union Student
{ char nume[20];
char prenume[20];
int varsta;
char sex;
float m_bac;
};
main()
{
union Student stud;
printf("Inserati Nume:"); gets(stud.nume);
printf("\n\nNumele studentului este:%s\n", stud.nume);
printf("Inserati Prenume:"); gets(stud.prenume);
printf("Prenumele studentului este:%s\n", stud.prenume);

206
Algoritmică şi programare

printf("Inserati varsta:"); scanf("%d", &stud.varsta);


printf("Varsta studentului este:%d\n", stud.varsta);
fflush(stdin); //curatarea bufferului
printf("Inserati sexul (M/F):"); scanf("%c", &stud.sex);
printf("Sexul studentului este:%c\n", stud.sex);
printf("Inserati media obtinuta la BAC:"); scanf("%f",
&stud.m_bac);
printf("Media obtinuta la BAC este:%.2f\n", stud.m_bac);
}

Afișarea in acest caz este corecta deoarece de fiecare data este utilizat
doar un membru al uniunii.

Fig. 63 Afișare uniune


Exemplu:
#include <stdio.h>
#include <conio.h>
#include <string.h>
union student
{ char nume[20];
char disciplina[20];
float media;
};
int main()
{
union student inregistrare1;
union student inregistrare2;
// inserare valori in inregistrare 1 – se observa alterarea datelor
strcpy(inregistrare1.nume, "Radu");
strcpy(inregistrare1.disciplina, "Matematica");
inregistrare1.media = 8.50;

207
Algoritmică şi programare

printf("Union inregistrare1 values example\n");


printf(" nume : %s \n", inregistrare1.nume);
printf(" disciplina : %s \n", inregistrare1.disciplina);
printf(" media : %f \n\n", inregistrare1.media);
// inserare valori in inregistrare2
printf("Union inregistrare2 values example\n");
strcpy(inregistrare2.nume, "Manuel");
printf(" nume : %s \n", inregistrare2.nume);
strcpy(inregistrare2.disciplina, "Fizica");
printf(" disciplina : %s \n", inregistrare2.disciplina);
inregistrare2.media = 9.50;
printf(" media : %f \n", inregistrare2.media);
return 0;
}
Structuri și uniuni. Diferențe
Se înlocuiește cuvântul cheie struct cu union. Din exemplele
anterioare rezultă că aceeași zonă de memorie poate fi interpretată în mod
diferit.

Nr.crt Structuri Uniuni

1 Se aloca spațiu separate pentru toți Se aloca un spațiu comun de memorie


membrii structurii egala cu cea mai mare dimensiune a
unui membru
2 Structura ocupa mai mult spațiu Uniunile ocupa mai puțin spațiu
(=suma dimensiunilor tuturor
membrilor)
3 Se pot accesa toți membrii structurii Se poate accesa cate1 membru
simultan

4 struct student union student


{ {
int nr_matricol; int nr_matricol;
char nume[10]; char nume[10];
float medie; float medie;
}; };
5 Spațiul total ocupat de structura= Spațiul total ocupat de uniune=
4+10+4=18 octeți (Bytes) max(4,10,4)=10 octeți (Bytes)

208
Algoritmică şi programare

Exemplu:

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

union Data1 {
int i;
float f;
char sir1[20];
};
struct Data2 {
int j;
float k;
char sir2[20];
};

int main( ) {
union Data1 u1;
printf( "Memory size occupied by union : %d\n",
sizeof(u1));
struct Data2 u2;
printf( "Memory size occupied by struct : %d\n",
sizeof(u2));
return 0;
}

209
Algoritmică şi programare

Directive pre-procesor
Limbajul C/C++ prezintă o particularitate în raport cu alte limbaje
compilate, prin aceea că dispune de un pre procesor înglobat în sistemul său
de compilare. Înainte de începerea traducerii, are loc o prelucrare inițială a
programului sursă, prin care sunt analizate eventuale opțiuni de lucru
transmise compilatorului.
Există un număr de operații care pot să fie realizate în această fază de
prelucrare inițială și care sunt formulate prin directive pre-procesor.
Anterior au fost deja întâlnite directivele #define (prin care s a dat un nume
unei constante) și #include (pentru introducerea unor fișiere sursă
suplimentare).
Pre-procesorul permite realizarea următoarelor operații:
• înlocuirea unor denumiri prescurtate prin șiruri de caractere,
operație denumită macro-substituție: directiva #define;
• includerea unor fișiere suplimentare în textul sursă: directiva
#include;
• compilarea selectivă a unor părți din programul sursă.

Directivele pre-procesor pot să apară oriunde în cadrul textului


programului: la începutul textului sursă, la sfârșitul textului, sau intercalat
printre instrucțiuni. O directivă este valabilă de la locul apariției, până la
sfârșitul fișierului.
Din exemplul anterior am reținut faptul că în limbajul C, nu exista
tipul Boolean, el fiind definit sub forma:

#define BOOL char


#define FALSE 0
#define TRUE 1
Macro-definiții
Macro definițiile sunt similare funcțiilor, prin aceea că permit
reprezentarea unor operații simple printr-un nume simbolic. Folosirea
macro-definițiilor este de obicei, un compromis între timpul de execuție și
spațiul de memorie ocupat.
Principalele avantaje ale macro-definițiilor sunt:
• Macro-definiția este executată mai rapid decât o funcție;
• nu există nici o restricție în ceea ce privește tipul parametrilor
transmiși unei macro-definiții, astfel că aceeași macro-definiție
poate fi folosită pentru mai multe tipuri de date.

210
Algoritmică şi programare

Dezavantajele folosirii macro-definițiilor:


• de fiecare dată când se apelează o macro-definiție, aceasta este
expandată conducând la un program de dimensiuni mai mari
decât un program care folosește funcții corespunzătoare în
locul macro-definițiilor;
• corpul unei funcții este compilat o singură dată și toate
apelurile acelei funcții folosesc aceeași instrucțiune mașină,
fără a le repeta la fiecare apel, așa cum se întâmplă în cazul
macro-definițiilor;
• este mai greu să se corecteze un program care conține macro-
definiții, deoarece macro expandările introduc un nivel
suplimentar la traducerea programului sursă, iar codul obiect
obținut este diferit de programul sursă.
Directiva #define
Numele simbolic ales ca prescurtare a șirului de caractere reprezintă
„NUMELE MACRO-DEFINIŢIEI“, în timp ce textul asociat se numește
„corpul macro-definiției“.

#define IDENTIFICATOR sir_caractere

Prin convenție, numele unei macro-definiții se scrie cu litere mari,


pentru a putea deosebi un nume de macro-definiție de numele variabilelor,
care se scriu cu caractere mici. Șirul de caractere înlocuiește toate aparițiile
identificatorului găsite în cadrul programului.

Exemplu:
#define DIMENSIUNE 100
char sir[DIMENSIUNE];

va fi tradusă, în etapa de prelucrare inițială a textului, prin: char


sir[100];
Cea mai frecventă utilizare a macro definițiilor este pentru înlocuirea
unor valori numerice prin nume simbolice. Directiva #define poate să ia și o
altă formă, care permite asocierea unor argumente numelui macro definiției,
într un mod asemănător funcțiilor C/C++.

#define identificator(lista parametri)


corp_macro_definitie

211
Algoritmică şi programare

Exemplu:
#define PATRAT(a) ((a)*(a))
Instrucțiunea: k=PATRAT(5) este tradusă în faza de prelucrare inițială
în: k=((5)*(5)). Parametrul 5, a înlocuit parametrul a la expandarea macro-
definiției.
Comparație între macro-definiții și funcții C/C++
Din punct de vedere al efectului obținut, o macro-definiție nu se
deosebește cu nimic de o funcție, macro-definiția introdusă anterior
PATRAT(a) conduce la același rezultat ca și următoarea funcție:

int pătrat (int a)


{
return a*a
}

Argumentele unei macro-definiții nu sunt variabile și nu au nici un tip


asociat. În timp ce funcția pătrat(), așa cum a fost definită anterior, trebuie
să primească un parametru de tip întreg și transmite un rezultat de tip întreg,
la expandarea unei macro-definiții se pot furniza argumente de orice tip.
Directiva #undef
O macro-definiție introdusă printr-o directivă #define își păstrează
semnificația până la sfârșitul fișierului sursă, sau până când aceasta este
invalidată în mod explicit cu ajutorul directivei #undef.
Forma generală a directivei este:

#undef nume_macro_definitie

Directiva #undef se folosește, în scopul de a anula o macro-definiție,


pentru a o putea redefini.
Exemplu:
#define DIMENS1 10
#define DIMENS2 20
char matrice[DIMENS1][DIMENS2];
……….
#undef DIMENS1
#undef DIMENS2

212
Algoritmică şi programare

Directive de compilare condiționată


Este posibilă compilarea selectivă a unor anumite părți din programul
sursă și omiterea celorlalte părți ale programului, folosind directive de
compilare condiționată. Aceste directive sunt similare instrucțiunilor if-else
din limbajul C/C++.
Compilarea selectivă este folosită pe scară largă în cazul unor
programe de aplicații care urmează să fie adaptate diferitelor cerințe ale
utilizatorilor. Se pot elimina anumite părți din program și/sau reintroduce
prin simpla modificare a unei macro-definiții.
Aceste directivele sunt: #if, #else, #elif și #endif
Directivele #if, #else, #elif și #endif
Directiva #if realizează compilarea unei succesiuni de instrucțiuni din
textul sursă, în cazul în care condiția indicată prin directiva if este adevărată;
în caz contrar, instrucțiunile nu sunt luate în considerare de către compilator.
Sfârșitul blocului de instrucțiuni care urmează să fie compilat condiționat
este indicat printr-o directivă #endif.
Forma generală a directivei este:

#if expresie_constanta
………../*instrucțiuni C/C++*/
#endif

Exemplu:
#include <stdio.h>
#define DIMENSIUNE 20
int main(void)
{
#if DIMENSIUNE > 10
printf (“Compilarea s-a făcut pentru DIMENSIUNE
10 \n);
#endif
}

Directiva #else oferă o alternativă de compilare atunci când condiția


menționată în directiva #if nu este îndeplinită. Structura obținută este
similară instrucțiunii if else din limbajul C/C++.
Exemplul anterior poate să fie completat astfel:

#include <stdio.h>
#define DIMENSIUNE 5

213
Algoritmică şi programare

int main(void)
{
#if DIMENSIUNE>10
printf (“Compilarea s-a facut pentru DIMENSIUNE>10
\n");
#else
printf (“Compilarea s-a facut pentru DIMENSIUNE<10
\n");
#endif
}

Fiecărei directive #if i se asociază o singură directivă #endif. În


situația în care este nevoie să se compileze un bloc de instrucțiuni, din mai
multe alternative existente, se va folosi directiva #elif, al cărei nume este o
prescurtare de la else-if.
Directiva #elif conține și ea o condiție sub forma unei expresii
constante. Dacă rezultatul expresiei este adevărat, se va compila blocul de
instrucțiuni corespunzător acelei condiții și se vor neglija celelalte
alternative. În cazul în care condiția este falsă, se analizează următoarea
condiție.
Forma generală a directivei este:
#if expresie
……… /*instrucțiuni C/C++*/
#elif expresie 1
……… /*instrucțiuni C/C++*/
#elif expresie 2
……… /*instrucțiuni C/C++*/
……….
#elif expresie N
……… /*instrucțiuni C/C++*/
#endif

Exemplu:
#define SUA 0
#define ANGLIA 1
#define FRANTA 2
#define ROMANIA 3
#define TARA ROMANIA
#if TARA ==SUA
char moneda[]=”dolar”;
#elif TARA==ANGLIA
char moneda[]=”lire sterline”;

214
Algoritmică şi programare

#elif TARA==FRANTA
char moneda[]=”franci”;
#elif TARA==ROMANIA /*sau, #else*/
char moneda[]=”lei”;
#endif

Unei directive #if i se pot asocia oricâte directive #elif dorim, așa cum
se vede în exemplul anterior, dar numai o singură directivă #else.
Directivele #ifdef şi #ifnde
Directivele #if și #elif permit compilarea succesivă a unor părți din
programul sursă, pe baza valorii unei expresii constante. Există și o altă
posibilitate de a realiza o compilare condiționată și anume, în raport cu
existenta sau lipsa unei macro-definiții. În acest caz se folosesc directivele
#ifdef (care este prescurtarea de la „if defined“) și #ifndef (care este
prescurtarea de la „if not defined“).
Forma generală a directivei #ifdef este:

#ifdef nume_macro_definitie
……… /*instrucțiuni C/C++*/
#endif

Dacă macro-definiția cu numele menționat a fost definită anterior


printr-o directivă #define, succesiunea de instrucțiuni cuprinsă între #ifdef și
#endif este compilată.
În mod similar, directiva #ifndef realizează compilarea unei secvențe
de instrucțiuni atunci când o macro-definiție cu numele indicat nu a fost
definită anterior:

#ifndef nume_macro_definitie
……….. /*instructiuni C/C++*/
#endif

Atât directiva #ifdef cât și directiva #ifndef pot să fie combinate cu o


directivă #else.
Exemplu:
#ifdef TEST
printf(“TEST a fost definit. \n”);
#else
printf(“TEST nu a fost definit. \n”);
#endif

215
Algoritmică şi programare

Dacă există o macro-definiție cu numele TEST, se compilează prima


operație printf(); în caz contrar, este compilată cea de a doua operație de
scriere.
Indicarea sfârșitului blocurilor de instrucțiuni care urmează să fie
compilate, se face ca și în cazul directivei #if cu ajutorul directivei #endif.
Directiva #include
Directiva #include arată unde să se intercaleze conținutul unui fișier,
în textul sursă în care apare directiva. Directiva are una din următoarele
două forme:

#include <nume_fisier>
sau
#include " nume_fisier “

Dacă fișierul este indicat între paranteze unghiulare, acesta va fi căutat


într o listă de adrese (directoare) care sunt definite la instalarea
compilatorului. Atunci când numele fișierului este indicat între apostrofuri
duble, căutarea fișierului se face conform regulilor de căutare ale sistemului
de operare: se începe cu directorul curent de lucru; dacă fișierul nu este găsit
aici, se continuă căutarea cu directorii impliciți ce conțin fișierele de
includere (header).
Biblioteca de programe executabile a limbajului C/C++ cuprinde o
serie de fișiere care conțin definiții ale funcțiilor din bibliotecă, numite
fișiere „header“ (întrucât se plasează la începutul textului sursă). Atunci
când un program folosește funcții standard, este nevoie să se includă în
textul sursă și fișiere „header“ corespunzătoare, așa cum s-a procedat în
toate programele anterioare.
Programatorul poate să creeze fișiere „header“ proprii, conținând
informații comune mai multor fișiere sursă, cum ar fi: definiții de structuri
de date, macro-definiții, variabile globale necesare pentru comunicarea între
fișiere. Fișierele „header“ folosesc în mod tradițional extensia .h. Ele asigură
plasarea informațiilor comune într-un singur loc, pentru a elimina repetarea
lor în fiecare modul sursă. Aceasta contribuie la simplificarea programării și
facilitează modificările ulterioare.
Un fișier care este inclus printr-o directivă #include poate să conțină
în interiorul său alte directive #include. Se obțin astfel mai multe nivele de
includere succesive. O astfel de situație este corectă din punct de vedere
sintactic.

216
Algoritmică şi programare

Directiva #error
Pentru etapa de punere la punct a unui program, utilizatorul are la
dispoziție directiva #error. La întâlnirea unei directive #error compilarea se
oprește și se afișează mesajul de eroare menționat prin directivă.
Forma generală a directivei este:

#error mesaj_eroare
Mesajul de eroare conținut în directivă nu se pune între apostrofuri
duble.
Un exemplu de folosire a directivei pentru verificarea unor valori
incorecte la compilarea condiționată, este:

#if DIMENSIUNE_INTREG<16
#error DIMENSIUNE_INTREG prea mica
#endif

217
Algoritmică şi programare

Funcții de intrare/ieșire
Deși limbajul C/C++ nu conține nici o instrucțiune pentru citirea
datelor (intrare) de la utilizator sau afișarea rezultatelor (ieșire) pe ecran,
există un număr mare de funcții standard pentru operații de intrare/ieșire.
Unele dintre aceste funcții se găsesc în biblioteca stdio.h (stdio este un
acronim pentru STanDard Input Output) iar altele în conio.h (conio este un
acronim pentru CONsole Input Output). Totodată, în fișierul stdio.h sunt
definite constantele simbolice EOF (End Of File) și NULL. Aceste valori
sunt returnate de diverse funcții când prelucrarea datelor s-a finalizat
(datorită terminării datelor sau datorită unei erori).
Funcții de intrare/ieșire pentru caractere
Funcțiile de intrare/ieșire pentru caractere au o utilizare limitată, fiind
folosite pentru citirea și afișarea informației caracter cu caracter, fără nici o
prelucrare în prealabil.

Funcția Descriere
int putchar (int Funcția afișează caracterul cu codul ASCII c pe ecran.
c); Funcția returnează prin numele ei valoarea c sau EOF în caz de eroare.
Funcția are prototipul în fișierul stdio.h.
int getchar(); Funcția citește de la tastatură un singur caracter. Funcția getchar
așteaptă apăsarea tastei Enter înainte de a returna primul caracter
introdus. La următoarele apeluri, funcția returnează restul caracterelor
tastate înainte de apăsarea tastei Enter.
Funcția returnează prin numele ei codul ASCII al caracterului citit sau
EOF dacă s-au terminat datele sau a apărut o eroare. Funcția are
prototipul în fișierul stdio.h.
int getch(); Funcția citește fără ecou (fără a afișa pe ecran) un caracter de la
tastatură. Funcția poate fi folosită pentru suspendarea temporară a
execuției unui program. Funcția returnează codul ASCII al caracterului
citit sau EOF în caz de eroare. Funcția are prototipul în fișierul conio.h.
int getche(); Funcția citește cu ecou (cu afișare pe ecran) un caracter de la tastatură.
Funcția returnează codul ASCII al caracterului citit sau EOF în caz de
eroare. Funcția are prototipul în fișierul conio.h.
int puts (char Funcția afișează pe ecran șirul de caractere memorat la adresa s și trece
*s); pe linia următoare. Funcția returnează prin numele său un număr
nenegativ în caz de succes sau EOF în caz de eroare.
char*gets(char*s Funcția citește un șir de caractere de la tastatura până la apăsarea tastei
) Enter și îl transferă în memorie la adresa s. Funcția returnează adresa s
a șirului citit sau NULL dacă s-au terminat datele sau a apărut o eroare.
Tabel 12 Funcții de intrare/ieșire pentru caractere[1]

218
Algoritmică şi programare

Exemplu:
char c;
puts("Tastati un caracter si apasati Enter");
c = getchar();
putchar(c);
Funcții de intrare/ieșire cu format
Funcțiile cu formatare permit citirea și afișarea informației după o
prelucrare în prealabil a acesteia. Prelucrarea se realizează conform unor
coduri de format (descriptori de format) scrise de programator într-un șir de
caractere.

Funcția Descriere
int printf (char *frmt, Funcția scrie pe ecran șirul frmt în care codurile de format sunt
var); înlocuite cu valorile expresiilor marcate prin var.
Funcția returnează numărul de caractere scrise sau EOF în caz de
eroare.
int scanf (char *frmt, Funcția citește de la tastatură date conform șirului frmt și le
var); transferă în memorie la adresele marcate prin var. Funcția
returnează numărul de coduri de format prelucrate corect sau EOF
în caz de eroare.
Tabel 13 Funcții de intrare/ieșire cu format[1]
Exemplu:
int n;
printf("Introduceti n:");
scanf("%i", &n); // &n este adresa variabilei n
printf("Valoarea lui n este: %i\n", n);

Exemplu: Afișare tabela coduri ASCII. Programul listează pe fiecare


linie, în ordine crescătoare coduri ASCII în baza 10, 8 și 16, și caracterul
asociat acestora. După 22 de linii, execuția se suspendă până la apăsarea
unei tastei.

#include <stdio.h>
#include <conio.h>
int main()
{
int i;
puts("Cod 10\tCod 8\tCod 16\tCaracter");
for(i=0;i<=255;i++)
{
printf("%3i\t%#4o\t%#4X\t%c\n", i, i, i, i);

219
Algoritmică şi programare

if(i % 22 == 21)
{
getch();
puts("Cod 10\tCod 8\tCod 16\t
Caracter");
}
}
}
Funcții cu formatare pentru șiruri de caractere
Funcția sprintf funcționează similar cu funcția printf, cu excepția că
scrierea datelor se va face într-un șir de caractere și nu pe ecran. În mod
similar, funcția sscanf funcționează ca funcția scanf, cu excepția că citirea
datelor se va face dintr-un șir de caractere și nu de la tastatură.

Funcția Descriere
int sprintf (char* dest, char Funcția scrie în șirul dest șirul frmt în care codurile de
*frmt, ...); format sunt înlocuite cu valorile expresiilor marcate prin
trei puncte. Funcția returnează prin numele său numărul de
caractere scrise sau EOF în caz de eroare.
int sscanf (char* srs, char Funcția citește din șirul srs date conform șirului frmt și le
*frmt, ...); transferă în memorie la adresele marcate prin trei puncte.
Funcția returnează prin numele său numărul de coduri de
format prelucrate corect sau EOF în caz de eroare.
Tabel 14 Funcții cu formatare pentru șiruri de caractere[1]

Funcții pentru gestiunea timpului


Funcțiile predefinite pentru gestiunea timpului folosesc tipul de dată
tm definit prin structura tm definită în <time.h>. Aceste funcții lucrează cu
tipul de dată tm și cu sinonimele pentru tipul long clock_t, time_t şi size_t.

Structura tm are membri care definesc complet un moment de timp:

struct tm
{
int tm_sec; // secunda 0-59
int tm_min; // minut 0-59
int tm_hour; // ora 0-23
int tm_mday; // ziua din luna 1-31
int tm_mon; // luna din an 0-11
int tm_year; // an incepand cu anul 1900

220
Algoritmică şi programare

int tm_wday; // numarul zilei din saptamana 0-6 incepand


cu duminica
int tm_yday; // numarul zilei din an 0-365
int tm_isdst; // 1=se modifica ora (iarna/vara), 0= nu, <0
informatie indisponibila
};

Funcția Descriere
int clock (void); Funcția returnează prin numele său numărul de
milisecunde care au trecut de la lansarea în execuție a
unui program.
Pentru a măsura durata de execuție a unor secvențe de
program se folosesc două apeluri ale funcției și se
calculează diferența dintre timpul returnat la apelul 2 și
timpul returnat la apelul 1.
În cazul unei erori funcția returnează valoarea -1.
double difftime (time_t time2, Funcția returnează un număr real care reprezintă
time_t time1); diferența dintre două măsurători ale timpului time2-
time1.
time_t time (time_t * timer); Funcţia citeşte data curentă şi o converteşte în număr de
secunde care au trecut de la 1 ianuarie 1900 până la
momentul curent. Altfel spus, funcţia converteşte data şi
ora curentă la tipul time_t. Dacă funcţia se apelează cu
parametrul NULL ea returnează numărul de secunde de
mai sus.
tm * localtime (const time_t * Funcţia actualizează structura tm folosind ora curentă.
timer);
time_t mktime (struct tm * Funcţia converteşte conţinutul pointerului time_pointer
time_pointer); la structura tm la tipul time_t. Funcţia returnează timpul
echivalent care a trecut de la 1 ianuarie 1900 măsurat în
secunde până la momentul precizat de structura tm. În
caz de eroare funcţia returnează valoarea -1.
char * asctime (const struct tm Funcția convertește conținutul pointerului time_pointer
* time_pointer); la structura tm în formatul extern de afişare a datei
calendaristice şi anume şirul de caractere cu formatul zi
luna data.
char * ctime (const time_t * Funcția convertește conținutul pointerului la tipul time_t
timer); în formatul extern de afişare a datei calendaristice şi
anume şirul de caractere cu formatul zi luna data. Funcţia
are acelaşi efect cu al funcţiei asctime. Deosebirea dintre
cele două funcţii constă în faptul că asctime foloseşte
structura tm iar ctime foloseşte tipul time_t.
tm * gmtime ( const time_t * Funcția folosește tipul time_t și actualizează structura tm
timer ); cu data curentă corelată cu meridianul curent.

size_t strftime ( char * ptr, Funcţia construieşte un şir de caractere care poate să
size_t maxsize, const char * conţină un mesaj şi diverşi specificatori de format de
format, const struct tm * time ); afişare a datei şi orei curente în diverse formate de

221
Algoritmică şi programare

Funcția Descriere
afişare. Funcția returnează valoarea maxsize sau valoarea
-1 în caz de eroare. Parametrii funcției sunt:
• ptr pointer la tipul char - memorează șirul
specificatorilor de format;
• maxsize numărul maxim de caractere copiate în șirul
pointat de ptr;
• format şir de caractere - definește formatul de
afișare al datei curente;
• time pointer la tipul tm.
Tabel 15 Funcții pentru gestiunea timpului[1]

Specificator Semnificaţie Exemplu


1 2 3
%a Numele prescurtat al zilei din săptămână * Thu
%A Numele complet al zilei din săptămână * Thursday
%b Numele prescurtat al lunii * Aug
%B Numele complet al lunii * August
Thu Aug 23 14:55:02
%c Afişare dată şi oră *
2001
%d Afişare număr zi din lună (01-31) 23
%H Afişare oră în format de 24 de ore (00-23) 14
%I Afişare oră în format de 12 de ore (01-12) 02
%j Afişare număr de zi din an (001-366) 235
%m Afişare lună în format numeric (01-12) 08
%M Afişare minut (00-59) 55
%p Afişare AM sau PM PM
%S Afişare secundă (00-61) 02
Numărul săptămânii din an în care prima zi de
%U 33
duminică este prima zi din săptămână (00-53)
Ziua din săptămână în care ziua de duminică este
%w 4
ziua 0 (0-6)
Numărul săptămânii în care ziua de luni este prima
%W 34
zi din săptămână (00-53)
%x Format numeric de afişare a datei * 08/23/01
%X Format digital de afişare a orei * 14:55:02
%y Afişare an prescurtat cu două cifre (00-99) 01
%Y Afişare completă a anului 2001
%Z Afişare zonă orară CDT

222
Algoritmică şi programare

Specificator Semnificaţie Exemplu


%% Afişare character % %

Tabel 16 Specificatorii de format pentru afișarea timpului


Exemplu:

#include <stdio.h>
#include <string.h>
int main()
{
int day, year;
char zi[20], luna[20], data[100];
strcpy( data, "Luni Martie 24 2018" );
sscanf( data, "%s %s %d %d", zi, luna, &day, &year );
printf("%d %s %d = %s\n", day, luna, year, zi );
day=29; year=2020;
sprintf(data, "%d %d",day, year);
printf("%d %s %d = %s\n", day, luna, year, zi );
return(0);
}
Aplicații practice:
utilizarea funcției clock
Funcția măsoară timpul exprimat în milisecunde pentru durata
execuției secvenței.

#include <stdio.h>
#include <conio.h>
#include <time.h>
int main()
{
int i,ts,tf; //ts timp de start, tf timp final
ts=clock();
printf(" ts = %d \n",ts);
for(i=0;i<10000000;i++);
tf=clock();
printf(" tf = %d \n",tf);
printf(" Timp de executie in milisecunde = %d\n",tf-ts);
getch();
}

223
Algoritmică şi programare

utilizarea funcției difftime


Se citește un șir de caractere și se măsoară în secunde timpul de
tastare.

#include <stdio.h>
#include <conio.h>
#include <time.h>
int main ()
{
time_t start,end;
char buf [256];
double dif; // dif timp de executie
time (&start);
printf (" Tastati un sir de caractere ");
gets (buf);
time (&end);
dif = difftime (end,start); // dif=end-start
printf (" Ati tastat sirul %s \n",buf);
printf (" Timp de tastare %.2lf secunde \n", dif );
getch();
}
utilizarea funcției localtime
#include <stdio.h>
#include <conio.h>
#include <time.h>
int main()
{
char
*zile[]={"Duminica","Luni","Marti","Miercuri","Joi","Vineri","Sambata"};
char
*luna[]={"Ianuarie","Februarie","Martie","Aprilie","Mai","Iunie","Iulie",

"August","Septembrie","Octombrie","Noiembrie","Decembrie"};
time_t t;
tm *timp;
time(&t); // Citire data curenta
timp=localtime(&t); // Actualizare structura tm
printf(" Anul curent \t %d \n",timp->tm_year+1900);
printf(" Luna curenta \t %s luna numarul %d \n",luna[timp-
>tm_mon],timp->tm_mon+1);

224
Algoritmică şi programare

printf(" Ziua curenta \t %s ziua numarul %d \n",zile[timp-


>tm_wday],timp->tm_mday);
printf(" Ora curenta \t %d \n",timp->tm_hour);
printf(" Minutul curent \t %d \n",timp->tm_min);
printf(" Secunda curenta \t %d \n",timp->tm_sec);
getch();
}
utilizarea funcției asctime
#include <stdio.h>
#include <time.h>
#include <conio.h>
int main ()
{
time_t t;
tm * timp;
time (&t);
timp=localtime(&t); // Citirea datei si orei curente din calculator
printf ( " Data si ora curenta: %s", asctime(timp)); // Afisare data si
ora curenta
getch();
}
utilizarea funcției strftime
#include <stdio.h>
#include <conio.h>
#include <time.h>
int main ()
{
time_t t;
struct tm * timp;
char sir[80];
time(&t);
timp=localtime(&t); // citire ora locala
// Afiare ora in format de 24 ore, minut, secunda
//strftime (sir,80,"Acum este ora %H:%M:%S",timp);
strftime (sir,80,"Astazi este %d.%m.%y ora %H:%M:%S",timp);
puts (sir);
getch();
}

225
Algoritmică şi programare

Funcții pentru operații matematice


Biblioteca limbajului C/C++ pune la dispoziția utilizatorului un număr
de funcții specializate pentru operații matematice, grupate pe diverse
categorii. Aceste funcții au prototipurile în fișierul antet math.h, stdlib.h și /
sau complex.h.
Orice program care folosește astfel de funcții trebuie să conțină
următoarele directive preprocesor:
#include <math.h>
#include <stdlib.h>
#include <complex.h>

Pe lângă funcțiile specializate incluse in bibliotecile C/C++, dispunem


și de valorile unor constante remarcabile din matematică, definite prin
constante simbolice.
Constante simbolice

Fig. 64 Constante simbolice[1]

Funcții trigonometrice
Funcțiile din această categorie au unul sau doi parametri de intrare și
returnează prin numele lor valoarea funcției matematice corespunzătoare.

226
Algoritmică şi programare

Fig. 65 Funcții trigonometrice[1]

Funcții putere și radical


Funcțiile din această categorie au unul sau doi parametri de intrare și
returnează prin numele lor valoarea funcției matematice corespunzătoare.

Fig. 66 Funcții putere și radical[1]

Funcții exponențiale, logaritmice și hiperbolice


Funcțiile din această categorie au un singur parametru de intrare și
returnează prin numele lor valoarea funcției matematice corespunzătoare.

227
Algoritmică şi programare

Fig. 67 Funcții exponențiale, logaritmice și hiperbolice[1]

Funcții de conversie
Funcțiile de conversie au un singur parametru care constituie adresa
de memorie a unui șir de caractere. Funcțiile transformă șirul ASCII în
valoarea numerică corespunzătoare și returnează prin numele lor această
valoare.

Funcția Descriere
int atoi (const char* c) Convertește șirul de caractere de la adresa c la valoarea întreagă a
acestuia.
double atof (const Convertește șirul de caractere de la adresa c la valoarea reală
char* c) simplă precizie a acestuia.
Tabel 17 Funcții de conversie[1]

228
Algoritmică şi programare

Funcții parte întreagă, de rotunjire și de trunchiere


Funcțiile din această categorie au un singur parametru de intrare și
returnează prin numele lor valoarea funcției matematice corespunzătoare.

Fig. 68 Funcții parte întreagă, de rotunjire și de trunchiere[1]

Funcții modul
Funcțiile din această categorie au un singur parametru de intrare și
returnează prin numele lor valoarea funcției matematice corespunzătoare.

Fig. 69 Funcții modul[1]

Funcții pentru generarea numerelor aleatoare


Funcțiile din această categorie returnează prin numele lor valori
aleatoare.

Funcția Descriere
int rand () Funcția returnează la fiecare apel un număr natural aleatoriu.
void srand (unsigned s) Funcția inițializează valoarea de start a secvenței de numere
aleatoare generate cu funcția rand.
Tabel 18 Funcții pentru generarea numerelor aleatoare[1]

229
Algoritmică şi programare

Aplicații practice
Calculul valorilor unei funcții reale(cu o singură variabilă reală) în n
puncte, într-un interval dat
Fie funcția f: R → R

#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <conio.h>
double f(double x)
{
if(x<-1)
return x*exp(1-pow(x,2));
if(x>=-1 && x<=1)
return pow(x+1,1.0/3)* log(1+pow(x,2));
if(x>1)
return sin(x)*sinh(x);
}
int main()
{
double a,b,h,x;
int n,k;
printf("Extremitatile intervalului [a,b] \n");
printf(" a = ");
scanf(“%lf”, &a);
printf(" b = ");
scanf(“%lf”, &b);
printf("Numarul de puncte din intervalul (%f, %f) in care se
calculeaza f(x)=", a, b);
scanf("%d",&n);
h=(b-a)/n;
for(k=0,x=a; k<=n+1; x=a+k*h,k++)
printf("%d. f ( %f ) = %f \n",k+1,x,f(x));
getch();
}
Calculul ariei și perimetrului unui triunghi oarecare
#include <stdio.h>
#include <conio.h>
#include <math.h>
#include <stdlib.h>

230
Algoritmică şi programare

float p(float,float,float); // prototipul functiei p


float arie(float,float,float); // prototipul functiei arie

int main()
{
float a,b,c; // lungimile laturilor triunghiului
printf(" a = "); scanf("%f",&a);
printf(" b = "); scanf("%f",&b);
printf(" c = "); scanf("%f",&c);
if(a+b>c && a+c>b && b+c>a) // validare date de intrare
{
printf(" Perimetrul = %f \n",2*p(a,b,c));
printf(" Aria = %f \n",arie(a,b,c));
}
else
printf(" Eroare in date \n");
getch();
}
float p(float a, float b, float c) // calculul semiperimetrului
{
return (a+b+c)/2.;
}

float arie(float a, float b, float c) // calculul ariei – formula lui Heron


{
float semip; // semiperimetrul triunghiului
semip=p(a,b,c);
return sqrt(semip*(semip-a)*(semip-b)*(semip-c));
}

Ghicirea unui număr aleator dintr-un interval din n încercări


#include <stdio.h>
#include <conio.h>
#include <stdlib.h>
int main()
{
int n,i,nr,nra,terminat=0, max;
printf(" Ghiceste numarul dintr-un interval stabilit\n");
printf(" Stabiliti valoarea maxima a intervalului [0,m]; m= ");
scanf("%d",&max);
printf("Numar de incercari: ");

231
Algoritmică şi programare

scanf("%d",&n);
nra=rand()%max;//generare numar
for(i=1;i<=n && terminat==0;i++)
{
printf(" Tasteaza un numar natural intre 0 si %d : ", max);
scanf("%d",&nr);
if(nr<nra)
printf(" Prea mic \n");
if(nr>nra)
printf(" Prea mare \n");
if(nr==nra)
{
printf(" OK \n");
terminat=1;
}
}
if(terminat)
printf(" Ai castigat ! \n");
else
printf(" Ai pierdut ! Numarul corect era: %d\n",nra);
getch();
}
Funcții pentru operații cu șiruri de caractere
În limbajul C/C++, un șir de caractere este de fapt un vector de
caractere (masive de date unidimensionale de tip char), ultimul caracter
fiind caracterul NUL, care are codul ASCII 0. În general, șirurile de
caractere se scriu între ghilimele sub formă de secvențe de caractere
normale și secvențe escape (e.g. "acesta este un sir"). Tipul șirurilor de
caractere este char[], adică vector de caractere, dar având în vedere relația
între tablouri și pointer, putem scrie char*, adică pointer la date de tip
caracter.
Declararea masivelor de date unidimensionale (șirurilor de caractere)
de tip char

Char nume_sir[n];

nume_sir este declarat ca un șir de caractere (tablou cu valori de tip


char) care poate să stocheze (n-1) caractere + terminatorul de șir (caracterul
NULL) '\0'.

232
Algoritmică şi programare

De exemplu, cf. tabelului cu codurile ASCII, șirul de caractere


"Limbajul C/C++" este stocat în memoria calculatorului astfel: 76 105 109
98 97 106 117 108 32 67 47 67 43 43 0 0

#include <stdio.h>
#include <conio.h>
int main()
{
int i;
char sir[] = "Limbajul C/C++";
for(i=0; i<=15; ++i)
printf("%d ", sir[i]);
getch();
}

Dacă dorim ca șirurile de caractere pe care le afișăm să fie create


dinamic, în funcție de anumite condiții, sau să fie preluate de la utilizator,
sau să efectuăm anumite operații cu ele, atunci trebuie să folosim funcții
specializate pentru aceste lucruri.
Funcțiile standard pentru prelucrarea șirurilor de caractere se găsesc în
biblioteca string.h. În prototipurile acestor funcții, parametrul de tip int
reprezintă de fapt un caracter (sau un octet).
Exemplu: Citirea si scrierea elementelor unui vector de tip char

#include <stdio.h>
#include <conio.h>
int main()
{
char s[50]; // Declararea sirului de caractere s
puts(" Inserati sirul:");
gets(s);
printf(" Sirul inserat este: %s\n",s);
system("pause");
return 0;
}

Se numește lungime a unui șir de caractere numărul caracterelor din


șir. Terminatorul de șir nu intră în calculul lungimii șirului de caractere.
Funcția strlen
Funcția strlen are prototipul int strlen(char *s);

233
Algoritmică şi programare

Funcția primește la intrare adresa unui șir de caractere și returnează


prin numele său lungimea șirului. Dacă în memorie șirul s este “limbaj de
programare”, atunci prin apelul strlen(s) funcția strlen returnează valoarea
20.
Funcția strtok
Funcția este folosită pentru determinarea cuvintelor dintr-un șir de
caractere. Un cuvânt este un șir de caractere delimitat de unul sau doi
separatori anterior definiți.
Funcția strtok are prototipul: char * strtok(char * s1, char *s2);
în care s1 este adresa de memorie a șirului care va fi descompus în
cuvinte, iar s2 este adresa de memorie a șirului care conține separatorii.
Descompunerea are loc după mai multe apeluri ale funcției. La primul
apel parametrul s1 precizează șirul care va fi descompus. La următoarele
apeluri primul parametru este NULL. La fiecare apel funcția returnează prin
numele ei adresa de început (un pointer) a următorului cuvânt din șirul s1
sau pointerul NULL dacă nu a mai fost găsit nici un cuvânt.
Funcții pentru concatenare
Funcțiile pentru concatenarea șirurilor de caractere ca parametri tot
șiruri de caractere. Prin operația de concatenare a șirului s1 cu șirul s2 se
obține un nou șir s=s1s2 obținut prin alipirea lui s2 la s1, după s1,
concomitent cu ștergerea din memorie a terminatorului șirului s1.

Funcția Descriere
char* strcat (char *dest, Funcția concatenează șirul destinație, notat dest, cu șirul
char *src) sursă, notat src. Funcția returnează prin numele său adresa
șirului modificat (adică dest).
char* strncat (char *dest, Funcția adaugă cel mult n caractere din șirul sursă, src, la
char *src, unsigned n) sfârșitul șirului destinație, dest. Funcția returnează prin
numele său adresa șirului modificat (adică dest).
Tabel 19 Funcții pentru concatenare[1]
Funcții pentru copierea șirurilor de caractere

Funcția Descriere
char* strcpy (char *dest, Funcția copiază șirul sursă de la adresa src, la adresa dest.
char *src) Funcția returnează prin numele său adresa șirului destinație,
dest.
char* strncpy (char *dest, Funcția copiază cel mult n caractere din șirul sursă de la
char *src, unsigned n) adresa src, la adresa dest. Funcția returnează prin numele său
adresa șirului destinație. Numărul de caractere copiate este
minimul dintre n și strlen(src). Funcția returnează prin
numele său adresa șirului destinație, dest.

char* strdup (char *src) Funcția copiază șirul src într-o zonă de memorie alocată

234
Algoritmică şi programare

Funcția Descriere
dinamic cu funcția malloc; programatorul trebuie să elibereze
cu funcția free zona de memorie când nu o mai folosește.
Funcția returnează prin numele său adresa zonei de memorie
alocată.
Tabel 20 Funcții pentru copierea șirurilor de caractere[1]
Funcții pentru compararea a două șiruri de caractere
Compararea a două șiruri memorate la adresele s1 și s2 se face astfel:
se compară primul caracter din șirul s1 cu primul caracter din șirul s2.
Dacă primul caracter din s1 are cod ASCII mai mic decât primul
caracter din s2 compararea se termină cu concluzia că s1 < s2. Dacă primul
caracter din s1 are cod ASCII mai mare decât primul caracter din s2
compararea se termină cu concluzia că s1 > s2. Dacă primul caracter din s1
are același cod ASCII ca primul caracter din s2, la compararea următoarelor
două caractere din cele două șiruri. Se continuă procesul până la
determinarea a două caractere aflate pe poziții identice cu coduri ASCII
diferite, caz în care s1 < s2 sau s1 > s2, sau până la determinarea sfârșitului
celor două șiruri, caz în care cele două șiruri sunt identice.
Funcția Descriere
int strcmp (char *s1, char Funcția compară caracterele din șirurile de la adresele s1 și
*s2) s2.
Funcția returnează prin numele său valoarea întreagă 0 dacă
șirurile s1 și s2 sunt identice; o valoare întreagă negativă
dacă s1 < s2, o valoare întreagă pozitivă dacă s1 > s2.
int stricmp (char *s1, char Funcția compară caracterele din șirurile de la adresele s1 și
*s2) s2, ignorând deosebirea de cod ASCII dintre minuscule și
majuscule.
Funcția returnează prin numele său valoarea întreagă 0 dacă
șirurile s1 și s2 sunt identice; o valoare întreagă negativă
dacă s1 < s2, o valoare întreagă pozitivă dacă s1 > s2.
int strncmp (char *s1, char Funcția compară șirul format cu primele n caractere din s1
*s2, unsigned n) (notat s1n) cu șirul format cu primele n caractere din șirul s2
(notat s2n).
Funcția returnează prin numele său valoarea întreagă 0 dacă
primele n caractere din șirurile s1n și s2n sunt identice; o
valoare întreagă negativă dacă s1n < s2n, o valoare întreagă
pozitivă dacă s1n > s2n.
Dacă n>strlen(s1) şi / sau n>strlen(s2) atunci funcția compară
tot șirul s1 și / sau tot șirul s2 cu tot șirul s2 și / sau tot șirul
s1.
int strnicmp (char *s1, char Funcția este o combinație a funcțiilor stricmp și strnicmp.
*s2, unsigned n)
Tabel 21 Funcții pentru compararea a două șiruri de caractere[1]

235
Algoritmică şi programare

Funcții de căutare

Funcția Descriere
char* strchr (char *s, int c) Funcția caută caracterul c în șirul s. Dacă c nu a fost găsit
funcția returnează pointerul NULL. Dacă c a fost găsit,
funcția returnează adresa primei apariții a caracterului c în
șirul s.
char* strrchr (char *s, int Funcția caută caracterul c în șirul s. Dacă c nu a fost găsit
c) funcția returnează pointerul NULL. Dacă c a fost găsit,
funcția returnează adresa ultimei apariții a caracterului c în
șirul s.
char* strstr (char *s1, char Funcția caută șirul s2 în șirul s1. Dacă șirul s2 nu a fost găsit
*s2) funcția returnează pointerul NULL. Dacă șirul s2 a fost găsit,
funcția returnează adresa primei apariții a șirului s2 în șirul
s1.
char * strpbrk(char *s1, Funcția caută un caracter din șirul s2 în șirul s1. Dacă nici un
char *s2) caracter din s2 nu este în s1 funcția returnează pointerul
NULL. Dacă este găsit un caracter din s2 în s1 funcția
returnează adresa primei apariții în s1 a acestui caracter.
Tabel 22 Funcții de căutare[1]
Funcții de setare

Funcția Descriere
char* strlwr (char *s) Funcția transformă majusculele din șirul s în minuscule și
returnează prin numele său adresa șirului modificat (adică s).
char* strupr (char *s) Funcția transformă minusculele din șirul s în majuscule și
returnează prin numele său adresa șirului modificat (adică s).
char* strrev (char *s) Funcția inversează în memorie șirul s și returnează prin
numele său adresa șirului modificat (adică s).
char* strset (char *s, int c) Funcţia transformă toate caracterele şirului s în caracterul c și
returnează prin numele său adresa șirului modificat (adică s).
char* strnset (char *s, int c, Funcţia transformă primele n caractere ale şirului s în
unsigned n) caracterul c și returnează prin numele său adresa șirului
modificat (adică s).
Tabel 23 Funcții de setare[1]

Aplicații practice
Zilele săptămânii
#include <stdio.h>
#include <conio.h>
int main()
{
int n;

236
Algoritmică şi programare

char *zile[] = {"Eroare", "Luni", "Marti", "Miercuri", "Joi", "Vineri",


"Sambata", "Duminica"};
printf("Introduceti un numar intre 1 si 7:");
scanf("%i", &n);
if(n>=1 && n<=7)
printf(zile[n]);
else
printf(zile[0]);
getch();
}
Numărul de cuvinte dintr-un vector de tip char
#include "stdio.h"
#include "conio.h"
int main()
{
char sir[100];
int nc,i;
puts(" Sirul :");
gets(sir);
i=0;
if(sir[i]==' ' || sir[i]==',' || sir[i]==';')
nc=-1;
else nc=0;
while(sir[i]!='\0')
{
if(sir[i]==' ' || sir[i]==',' || sir[i]==';')
{
nc++;
i++;
while(sir[i]==' ' || sir[i]==',' || sir[i]==';')
i++;
}
i++;
}
nc++;
printf("Numar cuvinte in sir=%d \n",nc);
getch();
}
Identificarea unui caracter într-un vector de tip char
Afișarea numărului de caractere identice întâlnite.

237
Algoritmică şi programare

#include <stdio.h>
#include <conio.h>
int main()
{
int n=0,i=0;
char s[50],c; // Declararea sirului de caractere s
puts(" Inserati sirul:");
gets(s);
puts(" Caracterul ");
scanf("%c",&c);
while(s[i]!='\0')
{
if(s[i]==c)
n++;
i++;
}
if(n==0)
printf("Sirul %s nu contine caracterul %c \n",s,c);
else
printf(" Numarul de caractere %c din sirul %s este egal cu
%d \n",c,s,n);
getch();
//system("pause");
return 0;
}
Oglinda caracterelor dintr-un șir
#include <stdio.h>
#include <conio.h>
#include <string.h>
int main()
{
char mesaj[256];
int i, len;
printf("Introduceti un text:\n");
gets(mesaj);
len = strlen(mesaj);
for(i = 0; i<len/2; ++i) {
char temp = mesaj[i];
mesaj[i] = mesaj[len - i - 1];
mesaj[len - i - 1] = temp;
}

238
Algoritmică şi programare

printf("Rezultat:\n%s", mesaj);
getch();
}
Criptarea cu n deplasări ASCII într-un șir de caractere
#include <stdio.h>
#include <string.h>
#include <conio.h>
int main()
{
char mesaj[256];
int i, len, n;
printf("Introduceti un text:\n");
gets(mesaj);
printf("Introduceti n:");
scanf("%d", &n);
len = strlen(mesaj);
for(i = 0; i<len; ++i)
mesaj[i] += n;
printf("Rezultat:\n%s", mesaj);
getch();
}
Primul cuvânt din propoziție cu majuscula
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <conio.h>
int main()
{
char mesaj[256];
int i, len, frazanoua = 1;
printf("Introduceti un text:\n");
gets(mesaj);
len = strlen(mesaj);
for(i = 0; i<len; ++i)
if(isalpha(mesaj[i]))
if(frazanoua){
mesaj[i] = toupper(mesaj[i]);
frazanoua = 0;
}else{
mesaj[i] = tolower(mesaj[i]);
}

239
Algoritmică şi programare

else if(ispunct(mesaj[i]))
frazanoua = 1;
printf("Rezultat:\n%s", mesaj);
getch();
}
Citirea unui mesaj de la tastatură și afișarea unui extras din șir
#include <stdio.h>
#include <string.h>
#include <conio.h>
int main()
{
char mesaj[256], extras[256];
int i, n, l;
printf(„Introduceti un text:\n”);
gets(mesaj);
printf(„Pozitie de start: „);
scanf(„%i”, &n);
printf(„Lungime: „);
scanf(„%i”, &l);
i = 0;
do
{
extras[i] = mesaj[i+n];
++i;
} while(extras[i-1] != 0 && i<l);
printf(„Extras: %s”, extras);
getch();
}
Test palindrom
#include <stdio.h>
#include <string.h>
#include <conio.h>
int main()
{
char mesaj[256];
int i, len, pal = 1;
printf("Introduceti un text:\n");
gets(mesaj);
len = strlen(mesaj);
for(i = 0; i<len/2; ++i)
if(mesaj[i] != mesaj[len - i - 1])

240
Algoritmică şi programare

pal = 0;
if(pal == 1)
printf("Sirul este palindron.");
else
printf("Sirul nu este palindrom.");
getch();
}
Citirea unui șir de la tastatură și eliminarea primei apariții a unui subșir
citit
#include <stdio.h>
#include <string.h>
#include <conio.h>
int main()
{
char mesaj[256], subsir[50], *inceput;
int i, len;
printf("Introduceti un text:\n");
gets(mesaj);
printf("Introduceti subsirul:\n");
gets(subsir);
inceput = strstr(mesaj, subsir);
if(inceput != NULL)
{
len = strlen(subsir);
strcpy(inceput, inceput+len);
}
printf("Rezultat:\n%s", mesaj);
getch();
}
Afișează cuvintele dintr-un șir de caractere câte unul pe linie
#include <stdio.h>
#include <string.h>
#include <conio.h>
int main()
{
char sir[100], sep[10], *pozitie;
printf("Introduceti textul");
gets(sir);
printf("Introduceti separatorii");
gets(sep);
printf(" Cuvintele din text sunt: \n");

241
Algoritmică şi programare

pozitie=strtok(sir, sep);
while(pozitie)
{
printf("%s\n", pozitie);
pozitie=strtok(NULL,sep);
}
getch();
}
Ștergerea tuturor aparițiilor unui caracter dintr-un șir
#include "stdio.h"
#include "conio.h"
#include "string.h"
int sterg(char *x, char c)
{
int i,k,j,este;
i=0;este=0;
while(i<strlen(x))
{
j=i;
while(*(x+j)==c)
{
este=1;
for(k=j;*(x+k)!='\0';k++)
*(x+k)=*(x+k+1);
*(x+k+1)='\0';
}
i++;
}
return este;
}
int main()
{
char sir[200],sirm[200],car;
puts(" Sirul ");
gets(sir);
strcpy(sirm,sir);
puts(" Caracterul care va fi sters ");
scanf("%c",&car);
if(sterg(sir,car))
{
printf("Caracterul %c a fost garsit in sirul %s \n", car,sirm);

242
Algoritmică şi programare

puts(" Sirul modificat ");


puts(sir);
}
else
printf(" Caracterul %c nu a fost gasit in sirul %s \n",car,sir);
getch();
}
Inserarea unui șir sursă într-un șir destinație după caracterul aflat pe
poziția n
#include "stdio.h"
#include "conio.h"
void insert(char *d, char *s, int n)
{ int is,id,im;
char m[100];
im=0;
id=n+1;
while(*(d+id)!='\0')
{ m[im]=*(d+id);
id++;
im++;
}
id=n+1;
m[im]='\0';
is=0;
while(*(s+is)!='\0')
{ *(d+id)=*(s+is);
id++;
is++;
}
im=0;
while(*(m+im)!='\0')
{ *(d+id)=*(m+im);
id++;
im++;
}
*(d+id)='\0';
}
int main()
{ char sird[100],sirs[100];
int n;
puts(" Sirul sursa ");

243
Algoritmică şi programare

gets(sirs);
puts(" Sirul destinatie ");
gets(sird);
puts(" Pozitia de inceput a inserarii ");
scanf("%d",&n);
insert(sird,sirs,n);
puts(sird);
getch();
}
Înlocuirea unui caracter c dintr-un șir de caractere, cu un alt caracter
#include "stdio.h"
#include "conio.h"
#include "string.h"
int inlocuire(char *x, char cs, char cd)
{
int i,k,gasit;
i=0;
gasit=0;
for(i=0;i<strlen(x);i++)
{
if(*(x+i)==cs)
{
gasit=1;
*(x+i)=cd;
}
}
if(gasit)
return 1;
else
return 0;
}
int main()
{
char sir[200],cars,card;
puts(" Sirul ");
gets(sir);
puts(" Caracterul de inlocuit ");
cars=getche();
puts("\n Caracterul cu care inlocuiesc ");
card=getche();
if(inlocuire(sir,cars,card))

244
Algoritmică şi programare

{
puts("\n Sirul modificat ");
puts(sir);
}
else
printf("\n Caracterul %c nu a fost gasit in sirul %s
\n",cars,sir);
getch();
}
Funcții pentru clasificarea caracterelor
Biblioteca standard ctype.h dispune de diverse funcții pentru testarea
apartenenței unui caracter la o mulțime bine precizată de caractere. Toate
aceste funcții au un unic parametru de intrare a cărui apartenență la o
mulțime de caractere se testează și returnează prin numele lor o valoare
întreagă nenulă dacă caracterul aparține mulțimii la care face referire funcția
sau valoarea întreagă 0 în caz contrar.
Funcții de apartenență

Funcția Descriere
int isalpha (int c) Funcția testează apartenența caracterului c la mulțimea
literelor și returnează prin numele său valoarea 1 dacă c
aparține mulțimii și este majusculă, valoarea 2 dacă c
aparține mulțimii și este minusculă, valoarea întreagă 0 dacă
c nu este o literă.
int islower (int c) Funcția testează apartenența caracterului c la mulțimea
literelor mici și returnează prin numele său valoarea 2 dacă c
aparține mulțimii (este minusculă), valoarea întreagă 0 în caz
contrar.
int isupper (int c) Funcția testează apartenența caracterului c la mulțimea
literelor mari și returnează prin numele său valoarea 1 dacă c
aparține mulțimii (este majusculă), valoarea întreagă 0 în caz
contrar.
int isdigit (int c) Funcția testează apartenența caracterului c la mulțimea
cifrelor zecimale ({0,1,2,3,4,5,6,7,8,9}) și returnează prin
numele său valoarea 4 dacă c aparține mulțimii (este cifră
zecimală), valoarea întreagă 0 în caz contrar.
int isxdigit (int c) Funcția testează apartenența caracterului c la mulțimea
cifrelor hexazecimale
({0,1,2,3,4,5,6,7,8,9,a,A,b,B,c,C,d,D,e,E,f,F}) și returnează
prin numele său valoarea 128 dacă c aparține mulțimii (este
cifră hexazecimală), valoarea întreagă 0 în caz contrar.
Int isalnum (int c) Funcția testează apartenența caracterului c la mulțimea
literelor sau cifrelor zecimale și returnează prin numele său
valoarea 1 dacă c aparține mulțimii și este majusculă,

245
Algoritmică şi programare

Funcția Descriere
valoarea 2 dacă c aparține mulțimii și este minusculă,
valoarea 4 dacă c aparține mulțimii și este cifră zecimală și
valoarea întreagă 0 dacă c nu aparține mulțimii.
int isascii (int c) Funcția testează apartenența caracterului c la mulţimea
primelor 128 de caractere ASCII și returnează valoarea 1
dacă c are codul ASCII între 0 şi 127 şi valoarea 0 dacă c are
codul între 128 şi 255.
int isspace (int c) Funcția testează apartenența caracterului c la mulțimea
formată cu caracterele de spațiere (spațiu, tab, new line,
vertical tab, form feed sau carriage return) și returnează prin
numele său o valoare întreagă nenulă dacă c aparține
mulțimii, valoarea întreagă 0 dacă c nu este caracter de
spațiere.
int iscntrl (int c) Funcția testează apartenența caracterului c la mulțimea
caracterelor de control (mulțimea caracterelor care au codul
ASCII între 0,1,2,...,31 și 127) și returnează prin numele său
o valoarea întreagă 32 dacă c aparține mulțimii, valoarea
întreagă 0 dacă c nu este caracter de spațiere.
int ispunct (int c) Funcția testează apartenența caracterului c la mulțimea
formată din caractere de punctuație ({ ! „ # $ % & ‚ < > * + -
. , : ; = ? { } |~ [ ] ^}) și returnează prin numele său valoarea
întreagă nenulă 16 dacă c aparține mulțimii, valoarea întreagă
0 dacă c nu este caracter de punctuație.
int isgraph (int c) Funcția testează apartenența caracterului c la mulțimea
formată cu caracterele grafice (caractere care au o
reprezentare grafică având codurile ASCII între 33 și 126) și
returnează prin numele său o valoare întreagă nenulă dacă c
aparține mulțimii, valoarea întreagă 0 în caz contrar.
int isprint (int c) Funcția testează apartenența caracterului c la mulțimea
formată cu caracterele imprimabile (caractere care pot fi
tipărite) și returnează prin numele său o valoare întreagă
nenulă dacă c aparține mulțimii, valoarea întreagă 0 în caz
contrar.
Tabel 24 Funcții de apartenență[1]
Funcții pentru conversia caracterelor
Biblioteca standard ctype.h pune la dispoziția utilizatorului două
funcții pentru conversia majusculelor în minuscule și invers.
Funcția Descriere
int tolower (int c) Funcția testează dacă c este o majusculă. Dacă c este o
majusculă funcția returnează minuscula corespunzătoare.
int toupper (int c) Funcția testează dacă c este o minusculă. Dacă c este o
minusculă funcția returnează majuscula corespunzătoare.
Tabel 25 Funcții pentru conversia caracterelor[1]

246
Algoritmică şi programare

Aplicații practice
Definirea unei parole
Definim o parolă ca fiind un șir de caractere cu următoarele
proprietăți:
1. șirul de caractere are lungimea cel puțin 11;
2. șirul are cel puțin trei cifre zecimale;
3. șirul are cel puțin două majuscule;
4. șirul are cel puțin trei minuscule;
5. șirul are exact trei caractere de punctuație.

Respectând proprietățile enumerate, șirul de caractere A17b3.;:Fcd


este o parolă iar, șirul de caractere parola1234 nu este o parolă în sensul mai
sus precizat.
Astfel, vom scrie un program care citește un șir de caractere de la
tastatură, testează dacă poate fi o parolă și afișează un mesaj corespunzător
verificării proprietăților unei parole.
În elaborarea programului, fiecare dintre cele cinci proprietăți
corespunde unei funcții. În funcția principală main citim șirul de caractere,
valida cele cinci proprietăți și vom afișa mesajul corespunzător.

#include <stdio.h>
#include <conio.h>
#include <string.h>
#include <ctype.h>
// Prototipurile functiilor din program
int lung(char *);
int zec(char *);
int maj(char *);
int min(char *);
int punct(char *);
int parola(char *);
//functia principala
int main()
{
char sir[100];
printf(" Tastati sirul: ");
gets(sir);
if(parola(sir))
printf(" Sirul %s este o parola \n",sir);
else
printf(" Sirul %s nu este o parola \n",sir);

247
Algoritmică şi programare

getch();
}
int lung(char *s) // Functie pentru determinarea lungimii sirului
{
return strlen(s);
}
int zec(char *s) // Functie pentru determinarea numarului de cifre
zecimale
{
int i,z;
for(z=0,i=0;*(s+i) != '\0';i++)
if(isdigit(*(s+i)))
z++;
return z;
}
int maj(char *s) // Functie pentru determinarea numarului de
majuscule
{
int i,z;
for(z=0,i=0;*(s+i) != '\0';i++)
if(isupper(*(s+i)))
z++;
return z;
}
int min(char *s) // Functie pentru determinarea numarului de
minuscule
{
int i,z;
for(z=0,i=0;*(s+i) != '\0';i++)
if(islower(*(s+i)))
z++;
return z;
}
int punct(char *s) // Functie pentru determinarea numarului de
caractere de punctuatie
{
int i,z;
for(z=0,i=0;*(s+i) != '\0';i++)
if(ispunct(*(s+i)))
z++;
return z;
}

248
Algoritmică şi programare

// Functie pentru validare parola - returneaza 1 pentru parola valida si


0 pentru sir invalid
int parola(char *s)
{
if(lung(s)>=11 && zec(s)>=3 && maj(s)>=2 && min(s)>=3 &&
punct(s)==3)
return 1;
else return 0;
}
Conversia unui șir de caractere reprezentând un număr întreg într-o
valoare întreagă
#include <stdio.h>
#include <conio.h>
#include <ctype.h>
int ascii_int(char *); // Prototipul functiei ascii_int
int main()
{
char sir[100];
printf(" Tastati numarul: ");
gets(sir);
printf(" Numarul este %d \n",ascii_int(sir));
getch();
}

int ascii_int(char *s) // Functia de conversie

{
int i,nr,semn;
for(i=0;isspace(*(s+i));i++); // Se ignora spatiile
semn=(*(s+i)=='-')?-1:1; // Determinare semn
if(*(s+i)=='+' || *(s+i)=='-')
i++; // Salt peste semn
for(nr=0;isdigit(*(s+i));i++) // Conversie la cifre
nr=10*nr+(*(s+i)-'0'); // Constructie numar
return semn*nr;
}
Transformă toate minusculele unui șir de caractere în majuscule și invers
#include "stdio.h“ // Minuscule majuscule
#include "conio.h"
#include "ctype.h"

249
Algoritmică şi programare

void schimb_m_M(char x[]); // prototip functie minuscule in


majuscule
void schimb_M_m(char x[]); // majuscule in minuscule
int main()
{
char s[100];
puts(" Tastati un sir de caractere ");
gets(s);
schimb_m_M(s);
printf(" Transformarea tuturor minusculelor in majuscule\n");
puts(" Sirul modificat este ");
puts(s);
getch();
schimb_M_m(s);
printf(" Transformarea tuturor majusculelor in minuscule\n");
puts(" Sirul modificat este ");
puts(s);
getch();
}
void schimb_m_M(char x[]) // minuscule in majuscule
{
int i;
i=0;
while(x[i]!='\0')
{
x[i]=toupper(x[i]);
i++;
}
}
void schimb_M_m(char x[]) // majuscule in minuscule
{
int i;
i=0;
while(x[i]!='\0')
{
x[i]=tolower(x[i]);
i++;
}
}

250
Algoritmică şi programare

Primul cuvânt din fiecare frază să înceapă cu majusculă


#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <conio.h>
int main()
{ char mesaj[256];
int i, len, frazanoua = 1;
printf("Introduceti un text:\n");
gets(mesaj);
len = strlen(mesaj);
for(i = 0; i<len; ++i)
if(isalpha(mesaj[i]))
if(frazanoua)
{ mesaj[i] = toupper(mesaj[i]);
frazanoua = 0;
}else
{mesaj[i] = tolower(mesaj[i]);
}
else if(ispunct(mesaj[i]))
frazanoua = 1;
printf("Rezultat:\n%s", mesaj);
getch();
}
Determinarea numărului de cifre hexazecimale din șirul citit
#include <stdio.h>
#include <conio.h>
#include <ctype.h>
int hex(char *); // Prototipul functiei hex
int main()
{
char sir[100];
printf(" Tastati un sir de caractere: ");
gets(sir);
printf(" Sirul %s are %d cifre hexazecimale \n",sir,hex(sir));
getch();
}
// Definitia functiei hex. Returneaza prin numele sau numarul de cifre
hexazecimale ale sirului de la adresa s

251
Algoritmică şi programare

int hex(char *s)


{
int i,h;
for(h=0,i=0;*(s+i) != '\0';i++)
if(isxdigit(*(s+i)))
h++;
return h;
}
Înlocuirea cifrelor zecimale dintr-un șir cu caracterul ‚#’
#include <stdio.h>
#include <conio.h>
#include <ctype.h>
void replace(char *); // Prototipul functiei replace
int main()
{
char sir[100];
printf(" Tastati un sir de caractere: ");
gets(sir);
replace(sir);
printf(" Sirul modificat este: %s\n",sir);
getch();
}
void replace(char *s) // Definitia functiei replace
{
int i;
for(i=0;*(s+i) != '\0';i++)
if(isdigit(*(s+i)))
*(s+i)='#';
}
Înlocuirea caracterelor de spațiere dintr-un șir cu caracterul ‚#’
#include <stdio.h>
#include <conio.h>
#include <ctype.h>
void replace(char *); // Prototipul functiei replace
int main()
{
char sir[100];
printf(" Tastati un sir de caractere: ");

252
Algoritmică şi programare

gets(sir);
replace(sir);
printf(" Sirul modificat este: %s\n",sir);
getch();
}
void replace(char *s) // Definitia functiei replace
{
int i;
for(i=0;*(s+i) != '\0';i++)
if(isspace(*(s+i)))
*(s+i)='#';
}
Testează dacă cele două șiruri sunt într-o relație
#include <stdio.h>
#include <conio.h>
#include <ctype.h>
int ncz(char *); // Prototipul functiei ncz
int main()
{ char sir1[100],sir2[100];
printf(" Tastati un sirul 1: ");
gets(sir1);
printf(" Tastati un sirul 2: ");
gets(sir2);
if(ncz(sir1)==ncz(sir2))
printf(" Sirul %s este in relatia R cu sirul %s\n",sir1,sir2);
else
printf(" Sirul %s nu este in relatia R cu sirul %s\n",sir1,sir2);
getch();
}
int ncz(char *s) // Definitia functiei ncz. Functia determina numarul
de cifre zecimale din sirul s
{ int i,z;
for(z=0,i=0;*(s+i) != '\0';i++)
if(isdigit(*(s+i)))
z++;
return z;
}

253
Algoritmică şi programare

Alocarea dinamică a memoriei


O variabilă prezintă o clasă de memorare care rezidă fie din declarația
ei, fie implicit din locul unde este definită variabila.
Zona de memorie alocata unui program scris în limbajul C/C++
cuprinde 4 subzone:
1. Zona text, rezervată memorării codului programului
2. Zona de date, rezervată variabilelor globale
3. Zona stivă ,rezervată variabilelor locale (datele de manevră,
temporare)
4. Zona heap rezervată datelor alocate dinamic (memoria dinamică).

Alocarea datelor poate fi:


1. Statică atunci când variabilele sunt implementate în zona de date.
2. Auto atunci când variabilele sunt implementate în stivă.
3. Dinamică atunci când variabilele sunt implementate în heap.
Memoria se alocă dinamic (la execuție) în zona “heap” atașată
programului, dar numai la cererea explicită a programatorului, prin
apelarea unor funcții de bibliotecă(malloc, calloc, realloc).
Memoria este eliberată numai la cerere, prin apelarea funcției
“free”.
4. Registru atunci când variabilele sunt implementate într-un registru
de memorie.

Variabilele din clasa (de memorie) static se declară cu cuvântul


cheie static:
static tip v1,v2,...,vn;
în care v1,v2,...,vn sunt variabile iar tip este tipul comun celor n
variabile.
static definitie_functie
în care definitie_functie este o definiție de funcție.
Memoria necesară tuturor acestor variabile este alocată la compilare în
segmentul de date alocat programului și nu se mai poate modifica în cursul
execuției programului. Implicit, variabilele globale sunt statice precum
declararea de tip static a unor variabile locale, definite în cadrul funcțiilor. O
variabilă sau o funcție declarată static are durata de viață egală cu durata de
viață a programului. Clasa de memorie static determină ca o variabilă
(globală sau locală) sau o funcție să fie cunoscută unității unde a fost
definită, devenind astfel inaccesibilă altei unități, chiar prin folosirea
cuvântului cheie extern. Variabilele din clasa static pot fi variabile statice
interne sau statice externe.

254
Algoritmică şi programare

Variabilele statice interne sunt variabile locale funcției în care au


fost definite. Valorile variabilelor statice interne se păstrează pe toată durata
execuției programului. Nici o altă funcție nu poate modifica valorile
variabilelor statice interne ale funcției în care au fost definite.
Variabilele statice externe se definesc în exteriorul oricărei funcții.
Orice funcție poate modifica valorile variabilelor statice externe. Variabilele
statice externe sunt variabile globale numai pentru fișierul sursă în care au
fost definite.

Variabilele din clasa de memorie auto se declară cu cuvântul cheie


auto care este implicit:
auto tip v1,v2,...,vn;
în care v1,v2,...,vn sunt variabile de automatice cu tipul tip.
Memoria necesară acestor variabile este alocată automat, prin apelul
unei funcții, în zona stivă aferentă unui program și este eliberată automat la
terminarea execuției funcției. Variabilele din această clasă sunt variabile
locale funcției în care au fost definite. Dacă lipsește cuvântul cheie auto se
consideră în mod implicit clasa auto. Valorile variabilelor auto sunt
actualizate la fiecare execuție a funcției în care au fost definite aceste
variabile și se distrug la terminarea execuției funcției. Nici o altă funcție nu
poate modifica valorile variabilelor auto ale funcției în care au fost definite.
În funcții diferite pot fi definite variabile locale care au același identificator.

Variabilele din clasa de memorie extern se declară cu cuvântul


cheie extern:
extern tip v1,v2,...,vn;
în care v1,v2,...,vn sunt variabile iar tip este tipul comun celor n
variabile.
extern definitie_functie
în care definitie_functie este o definiție de funcție.
Variabilele din clasa extern sunt variabile globale. Orice funcție poate
modifica valorile variabilelor externe. Dacă o variabilă din clasa extern este
declarată în exteriorul oricărei funcții cuvântul cheie extern poate să
lipsească. Valorile variabilelor din clasa extern se păstrează pe toată durata
execuției programului. Funcțiile sunt considerate variabile externe cu
excepția cazurilor în care se precizează altceva. Variabilele externe se
folosesc pentru ca funcțiile să poată comunica între ele.

Variabilele din clasa registru se declară cu cuvântul cheie register:


register tip v1,v2,...,vn;
în care v1,v2,...,vn sunt variabile iar tip este tipul comun celor n
variabile.

255
Algoritmică şi programare

Variabilele din această clasă sunt variabile locale funcției în care au


fost definite și se comportă ca variabilele din clasa auto. Valorile
variabilelor din clasa registru se memorează în regiștrii microprocesorului în
scopul minimizării timpului de execuție a programului. Adresa unei
variabile registru nu poate fi referită.

În limbajul C/C++, dimensiunea fiecărei variabile trebuie cunoscută în


momentul compilării programului; pentru variabilele cu tip fundamental,
dimensiunea este furnizată implicit de tipul acestor; pentru masive,
dimensiunea este furnizată explicit prin dimensiunile acestora (și tipul
elementelor), etc.
Un vector static nu este neapărat orice vector care are dimensiunea
constantă. Un vector definit în cadrul unei funcții (cu excepția funcției
principale main) nu este static deoarece, pe toată durata de execuție a
programului, el nu ocupă memorie, chiar dacă dimensiunea lui este
declarată în momentul dezvoltării programului. Alocarea pe stivă a unui
vector definit într-o funcție este făcută în momentul apelului funcției
(activarea funcției), iar la terminarea execuției funcției, memoria ocupată de
vector este eliberată automat.
Pentru a lucra cu informații ale căror dimensiuni nu sunt cunoscute la
momentul compilării programului există două alternative:
1. declararea la compilare a unor dimensiuni care acoperă în mod sigur
necesarul de memorie
2. alocarea memoriei necesare în timpul execuției programului.

Funcțiile standard pentru gestiunea dinamică a memoriei se găsesc în


biblioteca malloc.h şi stdlib.h. Pentru a fi generale, aceste funcții lucrează
cu pointeri de tip void, deci este necesar ca programatorul să execute
conversii explicite de tip. Algoritmul general de lucru în cazul alocării
dinamice a memoriei constă în alocarea memoriei, folosirea ei și, în final,
eliberarea acesteia.
Funcțiile standard pentru gestiunea dinamică a memoriei

Funcția Descriere
void* malloc (unsigned Funcția alocă o zonă de memorie de dimensiune size octeți și
size) returnează prin numele său adresa zonei de memorie alocate
sau NULL în caz de eroare.
void* calloc (unsigned cnt, Funcţia alocă o zonă de memorie de dimensiune cnt*size
unsigned size) octeți și completează cu valoarea 0 conținutul acesteia.
Funcția returnează prin numele său adresa zonei de memorie
alocate sau NULL în caz de eroare.

256
Algoritmică şi programare

Funcția Descriere
void* realloc(void * ptr, Funcţia realocă zona de memorie indicată de pointerul ptr
unsigned size) astfel încât să aibă dimensiunea size octeți și alocă o zonă de
dimensiunea specificată de al doilea argument. Copiază la
noua adresă datele de la adresa veche precizată de primul
argument. Eliberează memoria de la adresa veche.
Funcția returnează prin numele său adresa zonei de memorie
realocate sau NULL în caz de eroare.
void free (void *ptr) Funcţia eliberează zona de memorie indicată de pointerul ptr.
Eliberarea memoriei cu funcţia free este nefolositoare la
terminarea unui program, deoarece înainte de încărcarea şi
lansarea în execuţie a unui nou program se eliberează
automat toată memoria heap.
Tabel 26 Funcții standard pentru gestiunea dinamică a memoriei[1]

Alocarea dinamică a memoriei


Se declara directiva preprocesor
#include<stdlib.h>

Se alocă memorie (conform cu dimensiunea cerută):


void *malloc(int numar_de_octeti);

Se alocă memorie (conform cu dimensiunea cerută)și se inițializează


cu zero zona alocată:
void *calloc(int n_items, int size);

Intrucât funcțiile de alocare nu știu tipul de date ce vor fi memorate la


adresa respectivă au rezultat void*. Totodată, funcțiile au ca argument
dimensiunea zonei de memorie alocată și ca rezultat adresa zonei de
memorie alocate (de tip void *). Funcțiile de alocare au rezultat NULL dacă
cererea de alocare nu poate fi satisfăcută (nu există un bloc contiguu de
memorie egal cu dimensiunea solicitată).
Se utilizează:
1. Operatorul sizeof (determină numărul de octeți necesar unui tip de
date);
2. Operatorul de conversie cast (adaptează adresa de memorie primită
de la funcție la tipul datelor memorate la adresa respectivă).

Realocă memorie de dimensiunea numar_de_octeti


void *realloc(void* adr,int numar_de_octeti);

Alocă o zonă de memorie de dimensiunea specificată de cel de-al


doilea argument Copiază datele de la vechea adresă la noua adresă.

257
Algoritmică şi programare

Eliberează memoria de la vechea adresă.


void free(void* adr);

Eliberează zona de memorie de la adresa adr. Este inutilă eliberarea


memoriei cu ajutorul funcției free la terminarea unui program, deoarece
toată memoria heap este eliberată la încărcarea și lansarea în execuție a unui
nou program.
Alocarea dinamică a memoriei utilizând pointeri
Se declara o variabila de memorie de tip pointer către tipul de data al
variabilei dinamice
<tip> * <nume_pointer>;

In momentul in care programul utilizează variabila dinamica, se cere


sistemului de operare alocarea unui spațiu de memorie in HEAP, cu ajutorul
operatorului new:
<nume_pointer> = new <tip>;

Daca in program nu mai este utilizata variabila dinamica, se cere


eliberarea spațiului de memorie alocat in HEAP cu ajutorul operatorului
delete:
delete <nume_pointer>;

Operatorii new si delete sunt operatori unari, deci au prioritatea si


asociativitatea acestor tipuri de operatori.
Aplicații practice
1. Program care alocă spațiu pentru o variabilă întreagă
dinamică, după citire și tipărire, spațiul fiind eliberat.

#include <stdlib.h>
#include <stdio.h>
int main(){
int *a;
a=(int *)malloc(sizeof(int));
if(a==NULL){
puts("Memorie insuficienta ");
return 1;
}
printf("Tastati un numar: ");
scanf("%d",a);
printf("Adresa pe heap a variabilei a=%p\n", a);

258
Algoritmică şi programare

printf("Adresa variabilei a=%p\n",&a);


printf("sizeof(*a)=%d \t sizeof(a)=%d \t sizeof(&a)=%d\n",sizeof(*a),
sizeof(a), sizeof(&a));
free(a); //eliberare memorie
printf("a(dupa eliberarea memoriei):%p\n",a);
system("pause");
return 0;
}

2. Program care alocă dinamic spațiu pentru un vector,


după citire și tipărire, spațiul fiind eliberat.

#include <stdlib.h>
#include <stdio.h>
#include <windows.h>
int main(){
int i,n,*A;
printf("Inserati numarul de elemente: ");
scanf("%d",&n);
A=(int *)malloc(n*sizeof(int));
if(A==NULL){
puts("*** Memorie insuficienta ***");
return 1; // revenire din main
}
for (i=0; i<n; i++)
{A[i]=i+1;}
for (i = 0; i < n; i ++)
{printf("%d ",A[i]);}
free(A); //eliberare spatiu
//system("pause");
return 0;
}

sau

#include <stdlib.h>
#include <stdio.h>
#include <windows.h>
int main(){
int i,n,*A;
printf("Tastati numarul de elemente: ");
scanf("%d",&n);

259
Algoritmică şi programare

A=(int *)calloc(n,sizeof(int));
if(A==NULL){
puts("Memorie insuficienta");
return 1
}
for (i = 0; i < n; i ++)
{printf("%d ",A[i]);}
free(A); //eliberare spatiu de memorie
system("pause");
return 0;
}

sau

#include <stdlib.h>
#include <stdio.h>
#include <windows.h>
int main(){
int i,n,*A;
printf("Tastati numarul de elemente: ");
scanf("%d",&n);
A=(int *)malloc(n*sizeof(int));//A=(int *)calloc(n,sizeof(int));
if(A==NULL){
puts("Memorie insuficienta");
return 1;
}
for (i = 0; i < n; i ++)
{printf("A[%d]: ",i);scanf("%d",&A[i]);
}
for (i=0; i<n; i++)
{printf("%d ",A[i]);}
free(A);
system("pause");
return 0;
}

3. Program care alocă dynamic spațiu pentru un vector,


după care redimensionam vectorul

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

260
Algoritmică şi programare

int main(){
int i,n,*A;
printf("Inserati numarul de elemente: ");
scanf("%d",&n);
A=(int *)malloc(n*sizeof(int));
if(A==NULL){
puts("Memorie insuficienta ");
return 1;
}
for (i=0; i<n; i++)
{A[i]=i+1;}
for (i=0; i<n; i++)
{printf("%d ",A[i]);}
int *B=(int *)realloc(A,2*n*sizeof(int));
//int *B=(int *)realloc(NULL,n*sizeof(int)); //echivalent cu malloc
//int *A=(int *)realloc(A,0); //echivalent cu free(A)
printf("Zona de memorie pentru A=%d\n Zona de memorie pentru
B=%d\n ",A,B);
for (i=0; i<2*n; i++)
{printf("%d ",B[i]);}
free(B); //eliberare spatiu
system("pause");
return 0;
}

4. Program care alocă spațiu pentru calculul sumei a 2


numere utilizând variabile dinamice, după citire și
tipărire, spațiul fiind eliberat.

#include <stdlib.h>
#include <stdio.h>
#include <windows.h>
int main(){
int *a,*b,*s;
a=(int *)malloc(sizeof(int));
b=(int *)malloc(sizeof(int));
s=(int *)malloc(sizeof(int));
if(a==NULL){
puts("Memorie insuficienta ");
return 1;
}
printf("Inserati valoarea lui a: "); scanf("%d",a);

261
Algoritmică şi programare

printf("Inserati valoarea lui b: "); scanf("%d",b);


*s=*a+*b; // calculul sumei
printf("Suma=%d \n",*s);
free(a); free(b); free(s);//eliberare spatiu
system("pause");
return 0;
}

5. Program care alocă spațiu pentru calculul sumei a n


numere utilizând variabile dinamice, după citire și
tipărire, spațiul fiind eliberat.

#include <stdlib.h>
#include <stdio.h>
int main(){
int i, nr_numere, *valori_numere,s=0;
printf("Calculul sumei pentru cate numere? : "); scanf("%d",
&nr_numere);
//valori_numere=(int *)malloc(nr_numere*sizeof(int));
valori_numere=(int *)calloc(nr_numere,sizeof(int));
if(valori_numere==NULL){
puts("Memorie insuficienta");
return 1;
}
for (i = 0; i < nr_numere; ++i)
{
printf("Introduceti valoarea a %d-a: ", i+1);
scanf("%d", &valori_numere[i]);
s=s+valori_numere[i]; // calculul sumei
}
printf("Suma=%d \n",s);
free(valori_numere);//eliberare spatiu
//system("pause");
return 0;
}

6. Sa se calculeze suma si produsul a 2 numere citite de la


tastatura. Obs: utilizam alocarea dinamica a variabilelor
elementare iar pentru referirea mărimilor utilizam
pointeri de tip float

#include <stdio.h>

262
Algoritmică şi programare

#include <stdlib.h>
#include <malloc.h>

main()
{
float *a, *b, *s, *p;
a=new float, b=new float, s=new float, p=new float;
printf("Inserati a: "); scanf("%f",a);
printf("Inserati b: "); scanf("%f",b);
*s=(*a+*b); *p=*a * *b; //calculul variabilelor dinamice
printf("Suma =%.2f, Produsul =%.2f\n", *s, *p);
delete a; delete b; delete s; delete p;
}

7. Sa se calculeze aria si perimetrul unui dreptunghi


cunoscând laturile sale. Obs: utilizam alocarea dinamica
a variabilelor elementare iar pentru referirea mărimilor
utilizam pointeri de tip int (mărimea laturilor) si float
(arie si perimetru)

#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
main()
{
int *a, *b; float *aria, *p; //declarare pointeri variabile input &
output
a=new int, b=new int, aria=new float, p=new float; //alocarea
memoriei pentru variabilele dinamice si citirea valorilor lor de la tastatura
printf("Inserati a: "); scanf("%d",a);
printf("Inserati b: "); scanf("%d",b);
*p=2*(*a+*b); *aria=*a * *b; //calculul variabilelor dinamice
printf("Aria dreptunghiului =%.2f, perimetrul =%.2f\n", *aria, *p);
//afisarea valorilor variabilelor dinamice
delete a; delete b; delete aria; delete p; //eliberarea zonei de memorie
alocata variabilelor dinamice
}

263
Algoritmică şi programare

Funcții pentru operații cu blocuri de memorie


Funcțiile permit prelucrarea simplă informației din memorie, la nivel
de octeți, dar cu o viteză ridicată. Funcțiile standard pentru manipularea
blocurilor de memorie se găsesc în bibliotecile string.h și mem.h. În
prototipurile acestor funcții, parametrul de tip int reprezintă de fapt un octet
(sau un caracter). Ele pot fi folosite atât pentru zone de memorie alocate
dinamic, cât și pentru zone de memorie alocate static.

Funcția Descriere
void* memcpy (void *dest, Funcția copiază cnt octeți din zona de memorie src în dest
void *src, unsigned cnt) (src și dest trebuie să fie disjuncte) și returnează prin numele
său adresa destinație dest.
void* memmove (void Funcția copiază cnt octeți din zona de memorie src în dest
*dest, void *src, unsigned (nu neapărat disjuncte)și returnează prin numele său adresa
cnt) sursă src.
void* memchr (void *src, Funcția caută valoarea c în primii cnt octeți din zona de
int c, unsigned cnt) memorie src și returnează prin numele său adresa octetului c
sau NULL dacă c nu a fost găsit.
void* memset (void *dest, Funcția scrie valoarea c în primii cnt octeți din zona de
int c, unsigned cnt) memorie dest și returnează prin numele său adresa destinație
dest.
int memcmp (void *src1, Funcția compară în ordine cel mult cnt octeți din zonele de
void *src2, unsigned cnt) memorie src1 și src2. Funcția returnează prin numele său
valoarea întreagă 0 dacă informația din src1 este identică cu
cea din src2; valoarea întreagă -1 dacă primul octet diferit
din src1 este mai mic decât octetul corespunzător din src2;
valoarea întreagă 1 dacă primul octet diferit din src1 este mai
mare decât octetul corespunzător din src2
Tabel 27 Funcții pentru operații cu blocuri de memorie[1]

Aplicații practice
Program utilizare funcţie memcpy
În program se definesc pointerii ps (sursa), pd și adrdest (destinaţia)
către tipul char. Se citește de la tastatură un număr natural n. Se alocă
dinamic n octeți pentru un șir de caractere (șirul sursă de la adresa ps).
Adresa de început a șirului sursă se obține cu funcția malloc și este atribuită
pointerului ps. Se citește șirul sursă. Se alocă dinamic n octeți pentru șirul
destinație la adresa returnată de funcția malloc și atribuită pointerului pd. Se
copiază cu funcția memcpy șirul de la adresa ps la adresa pd. Se afișează
șirul destinație și se eliberează memoria alocată.

#include <stdio.h>
#include <conio.h>
#include <mem.h>

264
Algoritmică şi programare

#include <malloc.h>
int main()
{
char *ps,*pd,*adrdest; // ps (sursa), pd si adrdest (destinatia)
pointeri la tipul char
int n;
printf(" Numar octeti de alocat ");
scanf("%d",&n);
if(ps=(char *)malloc(n*sizeof(char)))
{
printf(" S-au alocat %d octeti la adresa %p \n",n,ps);
printf(" Tastati un sir de caractere ");
fflush(stdin);
gets(ps);
if(pd=(char *)malloc(n*sizeof(char)))
{
printf(" S-au alocat %d octeti la adresa %p \n",n,pd);
adrdest=(char *)memcpy(pd,ps,n);
printf(" Sirul %s a fost copiat la adresa %p\n",ps,pd);
printf(" Sirul la adresa destinatie %p este %s \n",pd,pd);
free(pd); // Eliberare memorie destinatie
free(ps); // Eliberare memorie sursa
}
else
printf(" Alocare destinatie esuata \n");
}
else printf(" Alocare sursa esuata \n");
getch();
}
Program utilizare funcţie memmove
În program se definesc pointerii ps (sursa), pd și adrdest (destinaţia)
către tipul char. Se citește de la tastatură un număr natural n. Se alocă
dinamic n octeți pentru un șir de caractere (șirul sursă de la adresa ps).
Adresa de început a șirului sursă se obține cu funcția malloc și este atribuită
pointerului ps. Se citește șirul sursă. Se alocă dinamic n octeți pentru șirul
destinație la adresa returnată de funcția malloc și atribuită pointerului pd. Se
copiază cu funcția memmove șirul de la adresa ps la adresa pd. Se afișează
șirul destinație și se eliberează memoria alocată.

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

265
Algoritmică şi programare

#include <mem.h>
#include <malloc.h>
int main()
{
char *ps,*pd,*adrdest; // ps (sursa), pd si adrdest (destinatia)
pointeri la tipul char
int n;
printf(" Numar octeti de alocat ");
scanf("%d",&n);
if(ps=(char *)malloc(n*sizeof(char)))
{
printf(" S-au alocat %d octeti la adresa %p \n",n,ps);
printf(" Tastati un sir de caractere ");
fflush(stdin);
gets(ps);
if(pd=(char *)malloc(n*sizeof(char)))
{
printf(" S-au alocat %d octeti la adresa %p \n",n,pd);
adrdest=(char *)memmove(pd,ps,n);
printf(" Sirul %s a fost copiat la adresa %p\n",ps,pd);
printf(" Sirul la adresa destinatie %p este %s \n",pd,pd);
free(pd); // Eliberare memorie la destinatie
}
else
printf(" Alocare destinatie esuata \n");
}
else printf(" Alocare sursa esuata \n");
getch();
}
Program utilizare funcţie memchr.
În program se definesc pointerii pc și pcc către tipul char. Se citește de
la tastatură un număr natural n. Se alocă dinamic n octeți pentru un șir de
caractere. Adresa de început a șirului de caractere se obține cu funcția
malloc și este atribuită pointerului pc. Se citește un șir de caractere și apoi
un caracter c. Programul caută caracterul c în șirul citit și dacă este găsit,
afișează prima apariție a caracterului c în șirul de la adresa memorată de
pointerul pc. În funcția main se apelează funcția memchr(pc,c,n) care caută
caracterul c în primele n caractere ale șirului de la adresa pc și returnează
adresa primei apariții a caracterului c în șir, adresă memorată de pointerul
pcc. Calculând diferență pcc-pc se obține poziția în șir a primei apariții a
caracterului c în șirul citit.

266
Algoritmică şi programare

#include <stdio.h>
#include <conio.h>
#include <mem.h>
#include <malloc.h>
int main()
{
char *pc,*pcc,c; // pc si pcc pointeri la tipul char
int n;
printf(" Numar octeti de alocat: ");
scanf("%d",&n);
if(pc=(char *)malloc(n*sizeof(char)))
{
printf(" S-au alocat %d octeti la adresa %p \n",n,pc);
printf(" Tastati un sir de caractere ");
fflush(stdin);
gets(pc);
printf(" Tastati caracterul de cautat ");
c=getche();
if(pcc=(char *)memchr(pc,c,n))
{
n=pcc-pc;
printf("\n Caracterul %c apare in sirul %s la adresa %p
\n",c,pc,pcc);
printf(" Prima aparitie a lui %c in sirul %s este pe pozitia %d
\n",c,pc,n);
printf(" %p - %p = %d \n",pcc,pc);
}
else printf("\n Caracterul %c nu apare in sirul %s \n",c,pc);
}
else printf(" Alocare esuata \n");
free(pc); // Eliberare memorie
getch();
}
Program utilizare funcţie memset.
În program se definesc pointerii pd și adrdest (destinația) către tipul
char. Se citește de la tastatură un număr natural n. Se alocă dinamic n octeți
pentru un șir de caractere. Adresa de început a șirului de caractere se obține
cu funcția malloc și este atribuită pointerului pd. Se citește un caracter de
umplere c. Caracterul c va fi scris la adresa pd de n ori cu apelul funcției
memset(pd,c,n);. Se memorează terminatorul de șir la adresa pd+n și se

267
Algoritmică şi programare

afișează șirul de la adresa adrdest, care coincide cu adresa memorată de pd.


În final se eliberează zona de memorie alocată de funcția malloc.

#include <stdio.h>
#include <conio.h>
#include <mem.h>
#include <malloc.h>
int main()
{
char *pd,*adrdest,c; // pd (destinatia) si adrdest pointeri la tipul
char
int n;
printf(" Numar octeti de alocat ");
scanf("%d",&n);
if(pd=(char *)malloc(n*sizeof(char)))
{
printf(" S-au alocat %d octeti la adresa %p \n",n,pd);
printf(" Tastati caracterul de umplere ");
c=getche();
adrdest=(char *)memset(pd,c,n);
*(pd+n)='\0'; // Terminatorul de sir de caractere
printf("\n Sirul la adresa destinatie %p este %s \n",adrdest,pd);
free(pd); // Eliberare memorie la destinatie
}
else
printf(" Alocare destinatie esuata \n");
getch();
}
Program utilizare funcţie memcmp.
În program se definesc pointerii ps (sursa) şi pd (destinația) către tipul
char. Se definesc variabilele comp care va memora valoarea returnată de
funcția memcmp și lungime care va memora valoarea minimă dintre
lungimile celor două șiruri care se vor compara. Se citește de la tastatură un
număr natural n. Se alocă dinamic n octeți pentru șirul sursă de caractere
(șirul sursă de la adresa ps). Adresa de început a șirului sursă se obține cu
funcția malloc și este atribuită pointerului ps. Se citește șirul sursă. Se alocă
dinamic n octeți pentru șirul destinație la adresa returnată de funcția malloc
și atribuită pointerului pd. Se determină valoarea minimă dintre lungimile
celor două șiruri. Se apelează funcția de comparare memcmp (ps, pd,
lungime);. Funcția returnează prin numele său rezultatul comparației în
variabila comp. Programul afișează valoarea lui comp și în funcție de

268
Algoritmică şi programare

această valoare emite un mesaj referitor la rezultatul comparării celor două


șiruri de caractere. În final se eliberează zonele de memorie alocate dinamic
de funcția malloc.

#include <stdio.h>
#include <conio.h>
#include <mem.h>
#include <malloc.h>
int main()
{
char *ps,*pd; // ps (sursa) si pd (destinatia) pointeri la tipul char
int n,comp,lungime;
printf(" Numar octeti de alocat ");
scanf("%d",&n);
if(ps=(char *)malloc(n*sizeof(char)))
{
printf(" S-au alocat %d octeti la adresa %p \n",n,ps);
printf(" Tastati sirul sursa ");
fflush(stdin);
gets(ps);
if(pd=(char *)malloc(n*sizeof(char)))
{
printf(" S-au alocat %d octeti la adresa %p \n",n,pd);
printf(" Tastati sirul destinatie ");
gets(pd);
printf(" Sirul sursa %s \n",ps);
printf(" Sirul destinatie %s \n",pd);
lungime=(strlen(ps)<strlen(pd))?strlen(ps):strlen(pd);
comp=memcmp(ps,pd,lungime);
printf(" comp = %d \n",comp);
if(comp<0)
printf(" %s < %s \n",ps,pd);
if(comp==0)
printf(" %s == %s \n",ps,pd);
if(comp>0)
printf(" %s > %s \n",ps,pd);
free(pd); // Eliberare memorie la destinatie
free(ps); // Eliberare memorie la sursa
}
else
printf(" Alocare destinatie esuata \n");
}

269
Algoritmică şi programare

else printf(" Alocare sursa esuata \n");


getch();
}
Sortare crescătoare șiruri distincte de caractere
#include <stdio.h>
#include <conio.h>
#include <mem.h>
#include <string.h>
#include <malloc.h>
int main()
{ char *s1,*s2,*s3; // s1,s2 si s3 pointeri la tipul char
int n,comp1,comp2,comp3,l1,l2,l;
printf(" Numar octeti de alocat: ");
scanf("%d",&n);
if(s1=(char *)malloc(n*sizeof(char)))
{
printf(" S-au alocat %d octeti la adresa %p \n",n,s1);
printf(" Tastati sirul 1: ");
fflush(stdin);
gets(s1);
if(s2=(char *)malloc(n*sizeof(char)))
{
printf(" S-au alocat %d octeti la adresa %p \n",n,s2);
printf(" Tastati sirul 2: ");
gets(s2);
if(s3=(char *)malloc(n*sizeof(char)))
{
printf(" S-au alocat %d octeti la adresa %p \n",n,s3);
printf(" Tastati sirul 3: ");
gets(s3);
l1=(strlen(s1)<strlen(s2))?strlen(s1):strlen(s2);
l2=(strlen(s2)<strlen(s3))?strlen(s2):strlen(s3);
l=(l1<l2)?l1:l2;
comp1=memcmp(s1,s2,l1); comp2=memcmp(s2,s3,l2);
comp3=memcmp(s1,s3,l);
if(comp1<0 && comp2<0 && comp3<0)
printf(" %s < %s < %s \n",s1,s2,s3);
if(comp1<0 && comp2>0 && comp3<0)
printf(" %s < %s < %s \n",s1,s3,s2);
if(comp1>0 && comp2<0 && comp3<0)
printf(" %s < %s < %s \n",s2,s1,s3);

270
Algoritmică şi programare

if(comp1>0 && comp2<0 && comp3>0)


printf(" %s < %s < %s \n",s2,s3,s1);
if(comp1<0 && comp2>0 && comp3>0)
printf(" %s < %s < %s \n",s3,s1,s2);
if(comp1>0 && comp2>0 && comp3>0)
printf(" %s < %s < %s \n",s3,s2,s1);
free(s1); free(s2); free(s3); // Eliberare memorie
}
else printf(" Alocare sir 3 esuata \n");
}
else printf(" Alocare sir 2 esuata \n");
}
else printf(" Alocare sir 1 esuata \n");
getch();
}

271
Algoritmică şi programare

Fișiere I/O. Funcții pentru operații asupra fișierelor


Fișiere de date. Introducere
Fișierul este o colecție de date - de obicei de același tip, stocate pe
suport extern. Fișierele sunt entități ale sistemului de operare deoarece
numele lor respectă convențiile sistemului, fără legătură cu un anumit limbaj
de programare. Conținutul unui fișier este variabil (numărul de elemente
poate fi chiar nul) și poate fi format din texte, numere, informații binare
(executabile, numere, imagini sau sunete în format binar).
Fișierele se folosesc în principal pentru păstrarea permanentă a unor
date importante, dar și pentru memorarea unor date inițiale (de intrare) sau
date de ieșire numerice voluminoase, de interes pentru anumite programe.
De obicei prin fișier se înțelege fișier disc plasat pe un mediu de stocare
(magnetic, optic, etc.). Noțiunea de fișier include orice flux de date (I/O
stream) din exterior spre memorie și viceversa, practic privește transferului
de date.
Programatorul se referă la un fișier printr-o variabilă de un anumit tip,
funcție de limbajul folosit și chiar de funcțiile folosite în cadrul limbajul
C/C++.
Operațiile pentru prelucrarea fișierelor sunt:
- crearea fișierului;
- asignarea fișierului intern (logic) la unul extern (fizic);
- deschiderea fișierului;
- operații de acces la date
- închiderea fișierului.
Lucrul cu fișiere implică identificarea următoarelor caracteristici:
• tipurile fișierelor,
• metodele de organizare,
• modul de acces,
• tipul articolelor acceptate.
În limbajul de programare C/C++ tipul de fișiere este unic, sub forma
unui flux de octeți cu o organizare secvențială[ (înșiruire de octeți, fără nici
un fel de organizare sau semnificație). De asemenea, accesul la fișiere se
poate face secvențial (valabil doar pentru fișiere standard) sau direct.
Prelucrarea fișierelor se poate face cu ajutorul funcțiilor predefinite,
existente în bibliotecile limbajului.
Există două niveluri de prelucrare a fișierelor: inferior și superior.
Prelucrarea la nivel inferior (fără gestiunea automată a zonelor tampon de
intrare/ieșire) este dependentă de mașină și sistem ceea ce conduce la o
portabilitate scăzută a programelor. Prelucrarea la nivel superior (se folosesc

272
Algoritmică şi programare

funcții specializate de gestiune a fișierelor) asigură un grad ridicat de


portabilitate a programelor, motiv pentru care vom prezenta doar nivelul
superior de prelucrare a fișierelor.
Funcțiile de prelucrare la nivel superior a fișierelor tratează fluxul de
octeți acordându-i o semnificație oarecare. Din punctul de vedere al
prelucrării, la acest nivel există două tipuri de fișiere: fișiere text și fișiere
binare.
Fișierele text conțin o succesiune de linii separate prin caracterul
NewLine (\n). Fiecare linie are 0 sau mai multe caractere imprimabile sau
Tab.
Fișierele binare necesită un program care să cunoască și să
interpreteze corect datele din fișier (structura articolelor, respectiv
succesiunea de octeți). Citirea/scrierea se face cu ajutorul funcțiilor fread
sau fwrite.
Există fișiere standard, care sunt gestionate automat de sistem, dar
asupra cărora se poate interveni și în mod explicit.
Aceste fișiere sunt:
• fișierul standard de intrare stdin (STanDard INput);
• fișierul standard de ieşire stdout (STanDard OUTput);
• fișierul standard scriere mesaje de eroare stderr
(STanDard ERRor);
• fișierul standard asociat portului serial stdaux (STanDard
AUXiliary);
• fișierul standard asociat imprimantei cuplate la portul
paralel stdprn (STanDard PRiNter).

Fișierele standard pot fi redirectate conform convențiilor sistemului de


operare, cu excepția lui stderr care va fi asociat întotdeauna monitorului.
În lucrul cu fișiere (sau la orice apel de sistem), în caz de eroare în
timpul unei operații se setează variabila errno, definită în errno.h, stddef.h
și stdlib.h. Valorile posibile sunt definite în stdlib.h.
Specificatorul de fișier reprezintă un nume extern de fișier, conform
convențiilor sistemului de operare. Specificatorul de fișier poate să conțină
strict numele fișierului sau poate conține și calea completă pană la el.
Nivelul superior de prelucrare a fișierelor
La acest nivel, fișierul este considerat ca flux de octeți, din care
funcțiile de prelucrare preiau secvențe pe care le tratează într-un anumit fel
(sau în care inserează secvențe de octeți).
Un fișier se descrie ca pointer către o structură predefinită, structura
FILE descrisă în biblioteca stdio.h.

273
Algoritmică şi programare

Funcțiile folosite la acest nivel pot fi împărțite în următoarele


categorii:
• funcții de prelucrare generală;
• funcții de citire/scriere cu/fără format;
• funcții de citire/scriere pentru caractere;
• funcții de citire/scriere pentru șiruri de caractere.

Funcții de prelucrare generală


Funcțiile de prelucrare generală se aplică tuturor fișierelor, indiferent
de tipul informației conținute; prelucrarea efectuată de acestea nu are nici un
efect asupra conținutului fișierului.
Funcțiile de citire/scriere cu format se folosesc pentru citirea/scrierea
fișierelor text care conțin informație de tip text (linii de text, separate prin
perechea CR/LF, iar la sfârșit se găsește caracterul CTRL-Z).
Funcțiile de citire/scriere fără format se folosesc pentru citirea/scrierea
fișierelor binare.
Funcțiile de citire/scriere pentru caractere se folosesc pentru transferul
unui singur caracter între un fișier text și memorie.
Funcțiile de citire/scriere pentru șiruri de caractere se folosesc pentru
transferul unui șir de caractere între un fișier text și memorie.
Funcțiile de citire/scriere deplasează pointerul de citire/scriere al
fișierului, spre sfârșitul acestuia, cu un număr de octeți egal cu numărul de
octeți transferați fără a trece de sfârșitul fișierului.
Deschiderea și/sau crearea unui fișier
Crearea sau deschiderea unui fișier existent se realizează prin apelul
funcției fopen. Funcția returnează un pointer, numit descriptor de fișier, spre
o structură de tip FILE (în care sunt scrise date referitoare la fișierul
deschis) sau NULL dacă fișierul nu poate fi deschis.
Antetul funcției fopen este:
FILE* fopen(const char * identificator, const char*
mod);

Tipul de dată FILE conține informații referitoare la fișier și la


tamponul de transfer de date între memoria internă și fișier (adresa,
lungimea tamponului, modul de utilizare a fișierului, indicator de sfârșit, de
poziție în fișier). Toate referirile (operațiile) ulterioare la fișierul
creat/deschis se fac prin intermediul descriptorului de fișier.
Parametrul identificator este adresa zonei de memorie în care este
memorat numele extern al fișierului creat sau deschis. Numele extern al

274
Algoritmică şi programare

fișierului poate conține și calea de căutare a fișierului conform standardului


sistemului de operare.
Parametrul mod este un șir de caractere, format dintr-un singur
caracter sau mai multe caractere, care specifică modul de creare / deschidere
a fișierului. Parametrul mod face distincția dintre fișierele text și fișierele
binare.
Deschiderea și/sau crearea unui fișier text
Valorile parametrului mod și semnificațiile acestor valori pentru
crearea/deschiderea unui fișier text.

Mod Descriere
“w” sau “wt” Mod de scriere fișier text. Dacă fișierul a fost anterior creat, conținutul
fișierului este șters. Dacă fișierul nu a fost anterior creat, se creează un nou
fișier, singura operație permisă fiind scrierea în fișier.
“w+” sau Mod de scriere fișier text cu posibilitate de citire. Dacă fișierul a fost
“wt+” sau anterior creat, conținutul fișierului este șters. Dacă fișierul nu a fost
“w+t” anterior creat, se creează un nou fișier, în care se poate scrie și din care se
poate citi.
“r” sau “rt” Mod de citire fișier text. Dacă fișierul nu a fost anterior creat se generează
eroare, funcția fopen returnează pointerul NULL.
“r+” sau “rt+” Mod de citire fișier text cu posibilitate de scriere. Dacă fișierul nu a fost
sau “r+t” anterior creat se generează eroare, funcția fopen returnează pointerul
NULL.
“a” sau “at” Mod de adăugare fișiere text. Deschide un fișier existent pentru scriere la
sfârșit (extindere) sau îl creează dacă nu există. Este permisă numai
scrierea.
“a+” sau Mod de adăugare cu posibilitate de citire fișiere text. Deschide un fișier
“at+” sau existent pentru scriere la sfârșit (extindere) sau îl creează dacă nu există.
“a+t” Este permisă numai scrierea.
Tabel 28. Deschiderea și / sau crearea unui fișier text[1]

Deschiderea și/sau crearea unui fișier binar


Valorile parametrului mod și semnificațiile acestor valori pentru
crearea / deschiderea unui fișier binar.

Mod Descriere
“wb” Mod de scriere fișier binar. Dacă fișierul a fost anterior creat, conținutul
fișierului este șters. Dacă fișierul nu a fost anterior creat, se creează un nou
fișier, singura operație permisă fiind scrierea în fișier.
“wb+” sau Mod de scriere fișier binar cu posibilitate de citire. Dacă fișierul a fost
“w+b” anterior creat, conținutul fișierului este șters. Dacă fișierul nu a fost
anterior creat, se creează un nou fișier, în care se poate scrie și din care se
poate citi.

275
Algoritmică şi programare

Mod Descriere
“rb” Mod de citire fișier binar. Dacă fișierul nu a fost anterior creat se
generează eroare, funcția fopen returnează pointerul NULL.
“rb+” sau Mod de citire fișier binar cu posibilitate de scriere. Dacă fișierul nu a fost
“r+b” anterior creat se generează eroare, funcția fopen returnează pointerul
NULL.
“ab” Mod de adăugare fișiere binar. Deschide un fișier existent pentru scriere la
sfârșit (extindere) sau îl creează dacă nu există. Este permisă numai
scrierea.
“ab+” sau Mod de adăugare cu posibilitate de citire fișiere binar. Deschide un fișier
“a+b” existent pentru scriere la sfârșit (extindere) sau îl creează dacă nu există.
Este permisă numai scrierea.
Tabel 29 Deschiderea și/sau crearea unui fișier binar [1]
Dacă în parametrul mod nu este prezent nici caracterul b nici
caracterul t, modul considerat depinde de valoarea variabilei _fmode: dacă
valoarea este O_BINARY, se consideră fișier binar; dacă valoarea este
O_TEXT, se consideră fișier text.

Operaţia Fişier text Fişier binar


Creare “w” “wb”
Citire “r” “rb”
Citire cu posibilitate de nedefinit “r+b” sau “rb+”
scriere
Creare şi actualizare “w+” “rwb” sau “w+b” sau
“wb+”
Adăugare “a” nedefinit
Tabel 30 Deschiderea și/sau crearea unui fișier
Valoarea implicită a variabilei _fmode este valoarea O_TEXT, adică
implicit se consideră că tipul fișierului este text.
Pentru a crea un fișier text se definește un pointer la tipul FILE, se
apelează funcția fopen cu parametrul mod “w” sau “wt”, se atribuie valoarea
returnată pointerului la tipul FILE. Dacă valoarea returnată este pointerul
NULL operația de creare a eșuat.
La terminarea execuției programelor toate fișierele deschise în timpul
execuției sunt închise automat. Închiderea fișierelor se poate face și în
timpul execuției programelor utilizând funcțiile fclose și fcloseall.
Antetul funcției fclose este:

int fclose(FILE* f);

Funcția închide fișierul al cărui descriptor este f, primit ca parametru,


și returnează valoarea întreagă 0, în caz de succes, sau -1, în caz de eroare.

276
Algoritmică şi programare

Înainte de închiderea fișierului, sunt golite toate buffer-ele asociate lui.


Buffer-ele alocate automat de sistem sunt eliberate.
Antetul funcției fclose_all este:

int fcloseall();

Funcția închide toate fișierele deschise în momentul apelului ei.


Funcția nu are parametri de apel și returnează valoarea întreagă 0, în caz de
succes, sau -1, în caz de eroare. Înainte de închiderea fișierului, sunt golite
toate buffer-ele asociate lui. Buffer-ele alocate automat de sistem sunt
eliberate.

Exemple: Creare/închidere fișier

Ex1:
#include <stdio.h>
int main()
{
FILE *f; // Pointer la tipul FILE
if((f=fopen("F:\\temp\\fisier1.txt","w")) = NULL)
puts(" Fisierul nu a fost creat!");
else
puts(" Fisierul a fost creat!");
fclose(f); // Inchidere fisier
getch();
}
Ex2:
#include <stdio.h>
int main()
{
FILE *f; // Pointer la tipul FILE
f=fopen("F:\\temp\\fisier2.txt","w");
if(f== NULL)
puts(" Fisierul nu a fost creat!");
else
puts(" Fisierul a fost creat!");
fclose(f); // Inchidere fisier
getch();
}

277
Algoritmică şi programare

Ex3: Creare/inchidere fişier text


#include <stdio.h>
#include <conio.h>
int main()
{
FILE *f; // Pointer la tipul FILE
char fname[30]; // Identificatorul (numele extern) fisierului
printf(" Nume fisier: ");
gets(fname);
if((f=fopen(fname,"w")) = NULL)
printf(" Fisierul %s nu a fost creat \n",fname);
else
printf(" Fisierul %s a fost creat \n",fname);
fclose(f); // Inchidere fisier
getch();
}

Pentru a crea un fișier se definește un pointer la tipul FILE, se


apelează funcția fopen cu parametru mod “w”, se atribuie valoarea returnată
pointerului la tipul FILE. Dacă valoarea returnată este pointerul NULL
operația de creare a eșuat.

Ex4: Creare/inchidere fişier binar


#include <stdio.h>
#include <conio.h>
int main()
{
FILE *f; // Pointer la tipul FILE
char fname[30]; // Identificatorul (numele extern) fisierului
printf(" Nume fisier: ");
gets(fname);
if((f=fopen(fname,"wb")) = NULL)
printf(" Fisierul %s nu a fost creat \n",fname);
else
printf(" Fisierul %s a fost creat \n",fname);
fclose(f); // Inchidere fisier
getch();
}

Pentru a crea un fișier binar se definește un pointer la tipul FILE, se


apelează funcția fopen cu parametru mod “wb”, se atribuie valoarea

278
Algoritmică şi programare

returnată pointerului la tipul FILE. Dacă valoarea returnată este pointerul


NULL operația de creare a eșuat.
Poziționarea indicatorului de citire/scriere
Programatorul poate în orice moment să controleze poziția indicatorul
de citire/scriere în fișier cu ajutorul unor funcții specializate.
Poziționarea indicatorului de citire / scriere la începutul fișierului:

void rewind(FILE *f);

Funcția are un singur parametru de apel și anume descriptorul


fișierului implicat. Executarea funcției are ca efect poziționarea la începutul
fișierului f deschis anterior, resetarea indicatorului de sfârșit de fișier și a
indicatorilor de eroare. După apelul lui rewind poate urma o operație de
scriere sau citire din fișier.

Testarea sfârșitului de fișier se realizează prin apelul macro-definiției


feof:
int feof(FILE* f);

Macro-ul furnizează valoarea indicatorului de sfârșit de fișier asociat


lui f. Valoarea indicatorului este stabilită la fiecare operație de citire din
fișier. Valoarea returnată este întregul 0 dacă indicatorul are valoarea sfârșit
de fișier sau o valoare diferită de zero în caz contrar. Apelul lui feof trebuie
să fie precedat de apelul unei funcții de citire din fișier. După atingerea
sfârșitului de fișier, toate încercările de citire vor eșua, până la apelul
funcției rewind sau închiderea și apoi redeschiderea fișierului.
Determinarea poziției curente a indicatorului de citire / scriere:
int fgetpos(FILE* f, fpos_t * poziţie);
După apel, la adresa poziție se află poziția indicatorului de citire /
scriere din fișierul f, ca număr relativ al octetului curent. Primul octet are
numărul 0. Valoarea returnată poate fi folosită pentru poziționare cu funcția
fsetpos. În caz de succes funcția returnează valoarea 0, iar în caz de eroare,
o valoare nenulă.

long ftell(FILE* f);

Funcția returnează poziția în fișierul f a indicatorului de citire / scriere


în caz de succes sau -1L în caz contrar. Dacă fișierul este binar, poziția este
dată în număr de octeți față de începutul fișierului. Valoarea poate fi folosită
pentru poziționare cu funcția fseek.

279
Algoritmică şi programare

Modificarea poziției indicatorului de citire / scriere :

int fseek(FILE* f, long deplasare, int origine);

Parametrul deplasare reprezintă numărul de octeți cu care se


deplasează indicatorul în fișierul f, iar parametrul origine reprezintă referința
față de care se deplasează indicatorul de citire / scriere.
Parametrul origine poate avea valorile:
SEEK_SET (0) poziționare față de începutul fișierului;
SEEK_CUR (1) poziționare față de poziția curentă;
SEEK_END (2) poziționare față de sfârșitul fișierului.

Funcția returnează valoarea 0 în caz de succes (și uneori și în caz de


eșec). Se semnalează eroare prin returnarea unei valori nenule numai în
cazul în care fișierul cu descriptorul f nu este deschis.
Poziționarea absolută a indicatorului de citire / scriere se face cu funcția:

int fsetpos(FILE* f, const fpos_t poziţie);

Indicatorul de citire / scriere se mută în fișierul cu descriptorul f la


octetul cu numărul indicat de parametrul poziție (care poate fi o valoare
obținută prin apelul lui fgetpos). Ambele funcții (fgetpos și fsetpos)
resetează indicatorul de sfârșit de fișier.
Redenumirea sau mutarea unui fișier existent
Se realizează prin apelul funcției rename, care are următorul prototip:

int rename(const char* n_vechi, const char* n_nou);

Parametrul n_vechi reprezintă vechiul nume al fișierului, iar n_nou


reprezintă numele nou. Dacă numele vechi conține numele discului (de
exemplu C:), numele nou trebuie să conțină același nume de disc. Dacă
numele vechi conține o cale, numele nou nu este obligat să conțină aceeași
cale. Folosind o altă cale se obține mutarea fișierului pe disc. Folosind
aceeași cale (sau nefolosind calea) se obține redenumirea fișierului. Nu sunt
permise caracterele (?, *) în cele două nume.
În caz de succes se funcția returnează valoarea 0. În caz de eroare
funcția returnează valoarea -1.
Ștergerea unui fișier existent
Se poate realiza prin apelul funcției remove, care are următorul
prototip:
int remove(const char* identificator);

280
Algoritmică şi programare

Parametrul identificator reprezintă numele extern al fișierului și care


poate să conțină calea de căutare a fișierului.
Golirea explicită a zonei tampon a unui fișier se realizează prin apelul
funcției fflush, care are următorul prototip:

int fflush(FILE* f);

Dacă fișierul cu descriptorul f are asociat un buffer de ieșire, funcția


scrie în fișier toate informațiile din acesta, la poziția curentă. Dacă fișierul
are asociat un buffer de intrare, funcția îl golește. În caz de succes
returnează valoarea zero, iar în caz de eroare valoarea constantei simbolice
EOF (definită în stdio.h).
Înainte de a citi un șir de caractere de la tastatură, buffer-ul trebuie
golit pentru a preveni citirea unui șir vid (datorită unei perechi CR/LF
rămase în buffer de la o citire anterioară a unei valori numerice). Ștergerea
se realizează prin apelul: fflush(stdin);
Funcții pentru tratarea erorilor
Pentru tratarea erorilor se folosesc următoarele funcții: clearerr și
ferror.
void clearerr (FILE *f);

Funcția resetează indicatorii de eroare și indicatorul de sfârșit de fișier


pentru fișierul cu descriptorul f (se scrie valoarea 0). O dată ce indicatorii de
eroare au fost setați la o valoare diferită de 0, operațiile de intrare / ieșire vor
semnala eroare până la apelul lui clearerr sau rewind.

int ferror (FILE *f);

Este o macro-definiție care returnează codul de eroare al ultimei


operații de intrare / ieșire asupra fișierului cu descriptorul f sau 0 dacă nu s-a
produs eroare.
Exemplu:

#include <stdio.h>
#include <conio.h>
int main()
{
FILE *f;
f=fopen("test.ttt","w"); // Deschidere fisier text pentru scriere
getc(f); // se generează eroare la citire
if(ferror(f)) // Test de eroare

281
Algoritmică şi programare

printf("Eroare al citirea din test.ttt\n");


clearerr(f); // Reseteaza indicatorii de eroare si
sfirsit de fisier
fclose(f);
getch();
}
Funcții de citire/scriere fără format
Funcțiile se folosesc pentru citirea/scriere fișierelor binare. Acestea
efectuează transferuri de secvențe de octeți între memoria internă și un fișier
de pe disc, fără a interveni asupra conținutului sau ordinii octeților
respectivi.
Citirea dintr-un fișier binar se realizează prin apelul funcției fread,
care are următorul prototip:

size_t fread(void* adr, size_t dim, size_t n, FILE* f);

Funcția citește din fișierul f, de la poziția curentă, un număr de n


articole, fiecare de dimensiune dim, și le memorează, în ordinea citirii, la
adresa adr. Funcția fread returnează numărul de entități citite. În total se
citesc, în caz de succes, n*dim octeți. În caz de eroare sau când se întâlnește
sfârșitul de fișier, funcția returnează o valoare negativă sau 0; size_t este
definit în mai multe fișiere header (între care stdio.h) și este un tip de dată
folosit pentru a exprima dimensiunea obiectelor din memorie. Este
compatibil cu tipul unsigned.

Scrierea într-un fişier binar se poate realiza prin apelul funcției


fwrite, care are următorul prototip:

size_t fwrite(const void* adr, size_t dim, size_t n,


FILE* f);

Funcția scrie în fișierul f, începând cu poziția curentă, un număr de n


articole fiecare de dimensiune dim, aflate în memorie la adresa adr. Funcția
fwrite returnează numărul articolelor scrise cu succes. În caz de eroare
returnează o valoare negativă.

Transferul de date cu format controlat este realizat prin funcțiile:


int fprintf(FILE* f, const char* format[,…]);
int fscanf(FILR* f, const char* format[,…]);
Funcțiile
• fprintf () - fscanf ()

282
Algoritmică şi programare

• fputc() - fgetc()
• fputs() - fgets()
Funcțiile fprintf () și fscanf () sunt versiunea similara printf () și
fscanf (), aplicata fișierelor . Singura diferență fata de fprintf () și fscanf (),
este faptul că, primul argument este un pointer la structura de tip FILE.
Funcțiile fgetc() si fgets() citesc 1 caracter, respectiv n-1 caractere si
returnează valoarea citita sau EOF in caz de eroare.
Funcțiile fputc() si fputs() scriu 1 caracter, respectiv n-1 caractere si
returnează valoarea citita sau EOF in caz de eroare.
Exemplu: Creare/scriere/închidere fișier

#include <stdio.h>
int main(){
int n;
printf("Inserati un numar: ");
scanf("%d",&n);
FILE *f;
f=(fopen("F:\\temp\\student.txt","w")); //write
if(f==NULL)
printf("Err!");
else
fprintf(f,"Valoarea inserata=%d \n",n);
fclose(f);
return 0;
}

sau

#include <stdio.h>
int main(){
int n;
printf("Inserati un numar: ");
scanf("%d",&n);
FILE *f;
f=(fopen("F:\\temp\\student.txt",“a")); //apend
if(f==NULL)
printf("Err!");
else
fprintf(f,"Valoarea inserata=%d \n",n);
fclose(f);
return 0;
}

283
Algoritmică şi programare

Funcții de citire/scriere șir de caractere


Transferul de șiruri de caractere se efectuează prin funcțiile:

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


int fputs(const char* s, FILE *f);

Funcția fgets citește un șir de caractere din fișierul cu descriptorul f și


îl memorează la adresa s. Transferul se termină atunci când s-au citit n-1
caractere sau s-a întâlnit caracterul newline. La terminarea transferului, se
adaugă la sfârșitul șirului din memorie terminatorul de șir ‘\0’. Dacă citirea
s-a terminat prin întâlnirea caracterului newline, acesta va fi transferat în
memorie, caracterul nul fiind adăugat după el (spre deosebire de gets, care
nu îl reține). La întâlnirea sfârșitului de fișier (fără a fi transferat vreun
caracter) sau în caz de eroare fgets returnează NULL. În caz de succes
returnează adresa șirului citit (aceeași cu cea primită în parametrul s).
Funcția fputs scrie în fișierul f caracterele șirului de caractere
memorat la adresa s. Terminatorul de șir (‘\0’) nu este scris și nici nu se
adaugă caracterul newline. În caz de succes fputs returnează ultimul
caracter scris. În caz de eroare funcția returnează EOF.

Exemplu: Creare/adăugare/închidere fișier

#include <stdio.h>
int main(){
char sir[15];
FILE *f;
f=(fopen("F:\\temp\\student.txt","a"));
if(f==NULL)
printf("Err!");
else
puts("Inserati un string: ");
gets(sir);
fputs(sir,f);
fclose(f);
return 0;
}
Exemplu: Citire din fișier
#include <stdio.h>
main() {
FILE *f;

284
Algoritmică şi programare

char sir[255];
f = fopen("F:\\temp\\test1.txt", "r");
fscanf(f, "%s", sir);
printf("1 : %s\n", sir);
fgets(sir, 255, (FILE*)f);
printf("2: %s\n", sir);
fgets(sir, 255, (FILE*)f);
printf("3: %s\n", sir);
fclose(f);
}
Aplicații practice
1. Scrieți un program C care sa creeze un fișier cu numele
Studenti.txt pentru citirea numelui si CNP-ului unui număr
de studenți stabilit de utilizator.
#include <stdio.h>
int main(){
char name[50];
int cnp,i,n;
printf("Numarul de studenti: ");
scanf("%d",&n);
FILE *f;
f=(fopen("F:\\temp\\student.txt","w"));
if(f==NULL)
printf("Err!");
else
for(i=0;i<n;++i)
{
printf("Pentru studentul %d\nInserati numele: ",i+1);
scanf("%s",name);
printf("Inserati CNP: ");
scanf("%d",&cnp);
fprintf(f,"\nNume: %s \nCNP=%d \n",name,cnp);
}
fclose(f);
return 0;
}
2. Scrieți un program C care sa creeze un fișier cu numele
Studenti.txt pentru citirea numelui si CNP-ului unui număr
de studenți stabilit de utilizator. Daca fișierul exista, sa se
adauge informațiile in fișierul existent.

285
Algoritmică şi programare

#include <stdio.h>
int main(){
char name[50];
int cnp,i,n;
printf("Numarul de studenti: ");
scanf("%d",&n);
FILE *f;
f=(fopen("F:\\temp\\student.txt",“a"));
if(f==NULL)
printf("Err!");
else
for(i=0;i<n;++i)
{
printf("Pentru studentul %d\nInserati numele: ",i+1);
scanf("%s",name);
printf("Inserati CNP: ");
scanf("%d",&cnp);
fprintf(f,"\nNume: %s \nCNP=%d \n",name,cnp);
}
fclose(f);
return 0;
}

3. Să se scrie programul care citește un număr natural n cu


semnificația de număr de studenți, numele și nota fiecăruia
din cei n studenți, creează un fișier binar în care se scriu
datele celor n studenți, închide fișierul, deschide fișierul,
citește fișierul și afișează conținutul fișierului.

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

struct student {
char nume[30];
int nota;
}s; // Tipul de data student

int main() // Functia principala


{
FILE *f;
int n,i;
char idf[50];

286
Algoritmică şi programare

printf(" Identificator fisier : ");


gets(idf);
if((f=fopen(idf,"wb"))==NULL) // Creare fisier binar
printf(" Eroare deschidere sursa \n");
else
printf(" Numar studenti : "); // Preluare date de la tastatura
scanf("%d",&n);
for(i=1;i<=n;i++)
{
printf(" Nume student %d : ",i);
scanf("%s",&s.nume);
printf(" Nota studentului %s :",s.nume);
scanf("%d",&s.nota);
fwrite(&s,sizeof(s),1,f); // Scriere in fisier
}
fclose(f); // Inchidere fisier
if((f=fopen(idf,"rb"))==NULL) // Deschidere fisier binar
printf(" Eroare deschidere sursa \n");
else
printf(" Fisierul %s contine studentii \n",idf);
for(i=1;i<=n;i++)
{
fread(&s,sizeof(s),1,f); // Citire din fisier
printf(" Studentul %s are nota %d \n",s.nume,s.nota); // Afisare
}
fclose(f);
getch();
}

287
Algoritmică şi programare

Metode de programare
Metoda “Divide et Impera”
Metoda “Divide et Impera” (desparte și stăpânește) reprezintă o
metodă de elaborare a algoritmilor care constă în descompunerea problemei
de rezolvat în două sau mai multe subprobleme independente, similare
problemei inițiale, care la rândul lor, se descompun în două sau mai multe
subprobleme, până când se obțin subprobleme a căror rezolvare este directă
și nu mai necesită alte descompuneri.
Soluția problemei inițiale se obține prin combinarea soluțiilor
problemelor cu rezolvare directă în care a fost descompusă.
Metoda “Backtracking”
Metoda „Backtracking” reprezintă o metodă de elaborare a
algoritmilor care pentru rezolvarea anumitor probleme este necesară
desfășurarea unui proces de căutare a soluției aflate într-o anumită mulțime,
numită spațiul stărilor. Pentru fiecare element din spațiul stărilor este
definită o mulțime de acțiuni sau alternative. Momentul inițial în rezolvarea
problemei corespunde unei stări, numită inițială, iar soluțiile corespund
drumurilor în spațiul stărilor, de la cea inițială până la una finală. Procesul
de rezolvare a problemei poate fi imaginat ca o secvență de acțiuni care
asigură deplasarea, prin intermediul unei secvențe de stări, în spațiul stărilor,
din starea inițială la cea finală. În cazul anumitor probleme se dorește
obținerea unei singure soluții, altele solicită determinarea tuturor soluțiilor
sau determinarea unei soluții optime, dintr-un anumit punct de vedere,
numită soluție optimală.
Metoda “Divide et Impera”
Metoda presupune:
• Descompunerea problemei curente P in subprobleme
independente SPi.
• In cazul in care subproblemele SPi admit o rezolvare imediata se
compun soluțiile si se rezolva problema P. Altfel se descompun
in mod similar si subproblemele Spi
• soluția finala se obține c o m b i n â n d soluțiile subproblemelor
rezolvate separat.

Evident, nu toate problemele își găsesc rezolvarea prin utilizarea


acestei metode. Majoritatea algoritmilor “Divide et Impera” presupun
prelucrări pe tablouri (dar nu obligatoriu).
Aceasta metoda (tehnica) se poate implementa atât iterativ dar si
recursiv. Dat fiind ca problemele se împart in subprobleme in mod recursiv,

288
Algoritmică şi programare

de obicei împărțirea se realizează până când șirul obținut este de lungime 1,


caz in care rezolvarea subproblemei este foarte ușoara, chiar banala.
Pașii metodei
1. Descompunerea problemei in doua sau mai multe subprobleme;
DIVIDE
2. Rezolvare probleme separat; IMPERA
3. Combinarea rezultatelor parțiale;

Fig. 70 Metoda “Divide et Impera”

289
Algoritmică şi programare

Aplicații practice
Calculul sumei primelor n numere naturale (Suma Gauss)

Fig. 71 Suma Gauss


#include "stdio.h"
#include "conio.h“
int n;
int s(int k,int p);
void afis(int k,int p);
int main()
{
int k,p;
printf(" Inserati n: ");
scanf("%d",&n);
afis(1,n);
printf(" S = %d ",s(1,n));
getch();
}
int s(int k,int p)
{

290
Algoritmică şi programare

if(k==p)
return k;
else
return s(k,(k+p)/2)+s((k+p)/2+1,p);
}
void afis(int k,int p)
{
if(k==p)
printf(" S(%d,%d)=%d\n",k,k,s(k,k));
else
{
afis(k,(k+p)/2);
afis((k+p)/2+1,p);
printf(" S(%d,%d)=%d\n",k,(k+p)/2, s(k,(k+p)/2));
printf("S(%d,%d)=%d \n",(k+p)/2+1,p, s((k+p)/2+1,p));
}
}

291
Algoritmică şi programare

Calculul n factorial (n!)

Fig. 72 n factorial
#include "stdio.h"
#include "conio.h“
int n;
int fact(int k,int p);
void afis(int k,int p);
int main()
{
int k,p;
printf(" n = ");
scanf("%d",&n);
afis(1,n);
printf(" S = %d ",fact(1,n));
getch();
}
int fact(int k,int p)

292
Algoritmică şi programare

{
if(k==p)
return k;
else
return fact(k,(k+p)/2)*fact((k+p)/2+1,p);
}
void afis(int k,int p)
{
if(k==p)
printf("fact(%d,%d)=%d \n",k,k,fact(k,k));
else
{
afis(k,(k+p)/2);
afis((k+p)/2+1,p);
printf("fact(%d,%d)=%d \n",k,(k+p)/2, fact(k,(k+p)/2));
printf(" fact(%d,%d)=%d \n", (k+p)/2+1, p, fact((k+p)/2+1,p));
}
}
Determinarea maximului dintr-un vector de numere reale

Fig. 73 Maximul dintr-un vector de numere reale

#include <stdio.h>
#include <conio.h>
#include <stdlib.h>
int n;
typedef float sir[100];
sir x;
float max(sir x,int k,int p)
{
float m1,m2;
if(k==p)
return x[k];
else
{
m1=max(x,k,(k+p)/2);

293
Algoritmică şi programare

m2=max(x,(k+p)/2+1,p);
if(m1>=m2)
return m1;
else
return m2;
}
}
int main()
{
int k,p,i;
char c[10];
printf(" Numarul de componente ale vectorului n = ");
scanf("%d",&n);
for(i=1;i<=n;i++)
{
printf(" Termenul %d = ",i);
scanf("%f",&x[i]);
}
printf(" Maximul din vector este %4f \n",max(x,1,n));
getch();
}
Algoritmul de sortare Quicksort
#include <stdio.h> // Quicksort crescator
#include <conio.h>
#include <malloc.h>
// Prototipurile functiilor
void permut(float *,float *);
int pivot(int,int);
void quicksort(float *,int,int);
void citv(float *,int);
void afisv(float *,int);
void permut(float *x,float *y) // Functia de permutare a lui x cu y
{
float aux;
aux=*x;
*x=*y;
*y=aux;
}
int pivot(int i,int j) // Functia pentru alegerea pivotului
{
return (i+j)/2;

294
Algoritmică şi programare

}
void quicksort(float *x,int m,int n) // Functia quicksort
{
int i,j,k;
float cheie;
if(m < n)
{ k=pivot(m,n);
permut(&x[m],&x[k]);
cheie=x[m];
i=m+1;
j=n;
while(i <= j)
{ while((i <= n) && (x[i] <= cheie))
i++;
while((j >= m) && (x[j] > cheie))
j--;
if(i < j)
permut(&x[i],&x[j]);
}
permut(&x[m],&x[j]);
quicksort(x,m,j-1); // stanga
quicksort(x,j+1,n); // dreapta
}
}
void citv(float *x, int n) //Fct pt citirea vectorului
{
int i;
for(i=0;i<n;i++)
{
printf("v[%d] = ",i);
scanf("%f",x+i);
}
}
void afisv(float *x, int n) // Functia pentru afisarea vectorului
{
int i;
for(i=0;i<n;i++)
printf("v[%d] = %8.4f\n", i,*(x+i));

}
int main() // Functia principala
{

295
Algoritmică şi programare

int n; // n numarul de componente ale vectorului


float *v; // *v pointer la float, adresa vectorului
printf("Dimensiune vector = ");
scanf("%d",&n);
if((v=(float *)malloc(n*sizeof(float))) != NULL)
{
citv(v,n);
printf("\n Vectorul nesortat \n\n");
afisv(v,n);
quicksort(v,0,n-1); // Sortare
printf("\n Vectorul sortat crescator \n\n");
afisv(v,n) ;
}
else
printf(" Alocare esuata \n");
getch();
}
𝟐𝒌−𝟏
Calculul sumei ∑𝒏𝒌=𝟏 𝟑𝒌+𝟏
#include "stdio.h“// Suma cu metoda "Divide et Impera"
#include "conio.h“
int n;
float s(int k,int p);
int main()
{
int k,p;
printf(" n = ");
scanf("%d",&n);
afis(1,n);
printf(" Suma = %f ",s(1,n));
getch();
}
float s(int k,int p)
{
if(k==p)
return (2.*k-1.)/(3.*k+1.);
else
return s(k,(k+p)/2)+s((k+p)/2+1,p);
}
void afis(int k,int p)
{
if(k==p)

296
Algoritmică şi programare

printf(" S(%d,%d)=%f \n",k,k,s(k,k));


else
{
afis(k,(k+p)/2);
afis((k+p)/2+1,p);
printf(" S(%d,%d)=%f\n",k,(k+p)/2, s(k,(k+p)/2));
printf("S(%d,%d)=%f \n",(k+p)/2+1,p, s((k+p)/2+1,p));
}
}
𝟐𝒌+𝟐
Calculul produsului ∏𝒏𝒌=𝟏 𝟑𝒌+𝟑
#include "stdio.h" // Produs cu metoda "Divide et Impera"
#include "conio.h“
int n;
float prod(int k,int p);
void afis(int k,int p);
int main()
{
int k,p;
printf(" n = ");
scanf("%d",&n);
afis(1,n);
printf(" Produsul = %f ",prod(1,n));
getch();
}
float prod(int k,int p)
{
if(k==p)
return (2.*k+2.)/(3.*k+3.);
else
return prod(k,(k+p)/2)*prod((k+p)/2+1,p);
}
void afis(int k,int p)
{
if(k==p)
printf(" prod(%d,%d)=%f \n",k,k,prod(k,k));
else
{
afis(k,(k+p)/2);
afis((k+p)/2+1,p);
printf(" prod(%d,%d)=%f\n",k,(k+p)/2,prod(k,(k+p)/2));
printf(" prod(%d,%d)=%f \n",(k+p)/2+1,p,prod((k+p)/2+1,p));

297
Algoritmică şi programare

}
}
Metoda “Backtracking”
Metoda „Backtracking” reprezintă o metodă de elaborare a
algoritmilor care pentru rezolvarea anumitor probleme este necesară
desfășurarea unui proces de căutare a soluției aflate într-o anumită mulțime,
numită spațiul stărilor.
Considerăm un labirint având una sau mai multe ieșiri. Starea inițială
poate fi considerată orice cameră a labirintului, problema revenind la găsirea
unui drum din camera respectivă către una dintre ieșiri. Desfășurarea
procesului de căutare a unei stări finale presupune, la fiecare etapă, alegerea
opțiunii pentru o alternativă posibilă a stării curente și detectarea acelor stări
capcană din care nu mai este posibilă continuarea procesului, sau deja se
cunoaște excluderea atingerii unei stări finale. Detectarea stării capcană
trebuie să determine revenirea la starea din care s-a ajuns la ea și selectarea
unei noi opțiuni de continuare. În cazul în care nu mai există alternative care
să nu fi fost selectate anterior, o astfel de stare devine la rândul ei capcană și
pentru ea se aplică același tratament.
Prin soluție a problemei se înțelege o secvență de acțiuni care
determină tranziția din starea inițială într-o stare finală, fiecare componentă
a unui drum soluție reprezentând o alternativă din mulțimea de variante
posibile.
Cu alte cuvinte, x 1 este alternativa aleasă pentru starea inițială, x 2
este alternativa selectată pentru starea în care s-a ajuns pe baza opțiunii x 1 ,
x 3 este alternativa selectată pentru starea în care s-a ajuns pe baza opțiunii
x 2 ,…, iar x n este alternativa selectată pentru starea în care s-a ajuns pe baza
opțiunii x n-1 . După efectuarea acțiunii corespunzătoare alegerii alternativei
x n rezultă o stare finală.
Procesul de căutare a unui drum soluție revine la tentativa de
extindere a porțiunii de drum construit, alegând prima alternativă
disponibilă pentru starea curentă atinsă. Continuarea drumului poate fi
realizată până la atingerea unei stări finale sau până la întâlnirea unei stări
capcană (mulțimea vidă de alternative). Dacă este atinsă o stare capcană,
atunci este necesară revenirea la starea anterioară și selectarea următoarei
alternative disponibile acestei stări. Dacă nu mai există alternative
disponibile, atunci se inițiază o nouă revenire și așa mai departe.
În cazul în care există cel puțin încă o alternativă disponibilă, atunci se
reia procesul de extindere a drumului rezultat. În condițiile în care revenirea
poate conduce la atingerea stării inițiale și pentru ea nu mai există
alternative disponibile, se consideră că problema nu are soluție.

298
Algoritmică şi programare

Pentru implementarea căutării este necesară reținerea alternativei


selectate pentru fiecare stare atinsă până la cea curentă, astfel încât, în cazul
unei reveniri să fie posibilă alegerea alternativei următoare.
Cu alte cuvinte, procesul de căutare revine la tentativa de extindere a
drumului curent (pasul de continuare), cu eventuala revenire în cazul
atingerii unei stări capcană (pasul de revenire - back), memorând
alternativele selectate pentru fiecare stare intermediară atinsă (track). De aici
își are geneza numele metodei Backtracking.
Backtracking nerecursiv
Pentru implementarea metodei definim următoarele funcții.
1. Funcția succesor testează dacă mulțimea S k mai are elemente
și care atribuie variabilei sccs (succesor) valoarea 1 dacă
mulțimea S k mai are elemente sau valoarea 0 în caz contrar.

void successor(sir x,int k,int &sccs)


{
if(x[k]<n)
{
sccs =1;
x[k]=x[k]+1;
} else
as=0;
}

2. Funcția validare verifică dacă sunt satisfăcute condițiile


interne specifice problemei și care atribuie variabilei ev (este
valid) valoarea 1 dacă sunt satisfăcute condițiile interne sau
valoarea 0 în caz contrar. Implementarea acestei funcții diferă
de la o problemă la alta, în funcție de condițiile de continuare.

void validare(sir x,int k, int &ev)


{
ev=1;
for(i=1;i<=k-1;i++)
if(!conditie)
ev=0;
}

3. Funcția afișare care afișează o soluție rezultat:

void afisare(sir x, int k)

299
Algoritmică şi programare

{
int i;
printf(" ( ");
for(i=1;i<=k-1;i++)
printf("%d, ",a[x[i]]);
printf("%d ) \n",a[x[i]]);
}

4. Secvența Backtracking care codifică mecanismul de generare


a soluțiilor:

k=1;
x[k]=0;
while(k>0)
{
do
{
succesor(x,k,sccs);
if(sccs)
validare(x,k,ev);
}while(sccs && !ev);
if(sccs)
if(k==n) afisare(x,k);
else
{
k=k+1;
x[k]=0;
}
else k=k-1;
}
Backtracking recursiv
În variantă recursivă se definește funcția Backtracking:

void back(int k)
{
if(sol(k))
afisare(x,k);
else
{
init(k);
while(succesor(x,k,sccs))

300
Algoritmică şi programare

if(validare(x,k,ev))
back(k+1);
}
}

Funcția are un singur parametru de tip întreg, care este indice al


vectorului soluție. Funcția testează dacă s-a generat o soluție prin apelul
funcției sol. Definiția funcției sol este:

int sol(int k)
{
return k==n+1;
}

Dacă s-a obținut o soluție, aceasta este afișată de funcția afișare, cu


aceeași definiție ca în cazul nerecursiv. Dacă nu s-a obținut o soluție, se
inițializează nivelul k cu valoarea aflată înaintea tuturor valorilor posibile.
Inițializarea este făcută de funcția init:

void init(int k)
{
x[k]=0;
}

Funcția init efectuează inițializarea lui xk cu o valoare prin care se


indică faptul că, până la acel moment, nu a fost selectată nici o alternativă
pentru poziția k a vectorului x; După inițializare, se generează succesiv
toate valorile din mulțimea Sk.
Pentru generarea acestor valori se folosește funcția succesor,
modificată fată de cazul nerecursiv în sensul că returnează valoarea
variabilei de tip întreg sccs:

int succesor(sir x,int k,int &sccs)


{
if(x[k]<n)
{
sccs=1;
x[k]=x[k]+1;
}
else sccs=0;
return sccs;
}

301
Algoritmică şi programare

Pentru fiecare valoare generată se testează cu funcția validare


condițiile de continuare. Funcția validare este modificată fată de cazul
nerecursiv prin returnarea valorii de tip întreg a variabilei ev:

int validare(sir x,int k, int &ev)


{
ev=1;
for(i=1;i<=k-1;i++)
if(!conditie)
ev=0;
return ev;
}

Dacă condițiile de continuare sunt îndeplinite se generează următoarea


valoare pentru componenta k prin apelul recursiv al funcției back.
În funcția main() se apelează funcția back cu parametrul 1 deoarece
algoritmul pleacă de la componenta de indice 1.
Pașii metodei
Pentru determinarea unei singure soluții, metoda presupune
parcurgerea următorilor pași:
1. starea inițială x 1 a problemei este prima alternativă posibilă
pentru starea curentă;
2. dacă starea curentă rezultată prin alternativa x 1 este finală,
atunci soluția are o singură componentă, vectorul x= (x 1 ) și
stop;
3. altfel, este selectată prima alternativă din mulțimea de
acțiuni posibile pentru starea curentă, x 2 є S 2 ;
4. dacă secvența de alternative care a condus la starea curentă
este x=(x 1, x 2, …, x k ) , atunci:
5. dacă starea curentă este finală, soluția este vectorul x=(x 1, x 2,
…, x k ) şi stop;
6. dacă starea curentă nu este starea finală atunci:
7. dacă pentru starea curentă există alternative disponibile,
atunci se alege prima dintre ele şi se continuă;
8. altfel, se revine la starea anterioară celei curente, soluția
parțial construită devine x=(x 1, x 2, …, x k-1 ) și se face salt la
Pasul 7.
9. dacă, în urma unui pas de revenire, s-a ajuns la starea inițială
și nu mai sunt alternative disponibile, atunci problema nu are
soluție și stop.

302
Algoritmică şi programare

În cazul în care trebuie determinate toate soluțiile problemei, căutarea


continuă după determinarea fiecărei soluții prin efectuarea de reveniri
succesive. Terminarea căutării este decisă în momentul în care s-a revenit la
starea inițială și nu mai există alternative disponibile.
Aplicații practice
Problema celor n dame si numărul soluțiilor rezultat (Backtracking
recursiv)

#include <stdio.h>
#include <conio.h>
typedef int sir[100];
sir x;
int i,n,k,as,ev,nsol;
void afisare(sir x, int k);
void back(int k);
void init(int k);
int sol(int k);
int succesor(sir x,int k,int &as);
int validare(sir x,int k, int &ev);
int main()
{
nsol=1;
printf(" Problema celor n dame \n");
printf(" Numarul de dame n = ");
scanf("%d",&n);
back(1);
printf(" Numarul total de solutii rezultat este %d \n",nsol-1);
getch();
}
void init(int k)
{ x[k]=0;
}
int sol(int k)
{
return k==n+1;
}
int succesor(sir x,int k,int &as)
{
if(x[k]<n)
{
as=1;

303
Algoritmică şi programare

x[k]=x[k]+1;
}
else
as=0;
return as;
}
int validare(sir x,int k, int &ev)
{
ev=1;
for(i=1;i<=k-1;i++)
if(x[k]==x[i] || (k-i==abs(x[k]-x[i])))
ev=0;
return ev;
}
void afisare(sir x, int k)
{
for(i=1;i<=k-1;i++)
printf(" Dama %d este pe linia %d si coloana %d \n",i,i,x[i]);
}
void back(int k)
{
if(sol(k))
{
printf("Solutia %d\n",nsol);
nsol++;
afisare(x,k);
} else
{
init(k);
while(succesor(x,k,as))
if(validare(x,k,ev))
back(k+1);
}
}
Permutările unei mulțimi de n obiecte (Backtracking nerecursiv)
#include <stdio.h>
#include <conio.h>

typedef int sir[100];


sir a,x;
int i,k,n,as,ev,nf;

304
Algoritmică şi programare

void succesor(sir x,int k,int &as);


void validare(sir x,int k, int &ev);
void afisare(sir x, int k);
int main()
{ printf("Generarea permutarilor de n obiecte \n");
nf=0;
printf(" Numarul de obiecte n = "); scanf("%d",&n);
printf(" Tastati obiectele \n");
for(i=1;i<=n;i++)
{ printf(" a [ %d ] = ",i);
scanf("%d",&a[i]);
}
k=1;x[k]=0;
while(k>0)
{ do
{ succesor(x,k,as);
if(as) validare(x,k,ev);
} while(as && !ev);
if(as)
if(k==n)
{ nf++;
afisare(x,k);
} else
{ k=k+1;
x[k]=0;
}
else k=k-1;
}
printf(" Numarul permutarilor de %d obiecte este egal cu %d
\n",n,nf);
getch();
}
void afisare(sir x, int k)
{
int i;
printf(" %d ( ",nf);
for(i=1;i<=k-1;i++)
printf("%d, ",a[x[i]]);
printf("%d ) \n",a[x[i]]);
}
void succesor(sir x,int k,int &as)
{

305
Algoritmică şi programare

if(x[k]<n)
{
as=1;
x[k]=x[k]+1;
}
else as=0;
}
void validare(sir x,int k, int &ev)
{
ev=1;
for(i=1;i<=k-1;i++)
if(!(x[k]!=x[i]))
ev=0;
}
Combinări de n obiecte luate câte p (Backtracking nerecursiv)
#include <stdio.h>
#include <conio.h>
typedef int sir[100];
sir a,x;
int p,i,k,n,as,ev,cnp;
void succesor(sir x,int k,int &as);
void validare(sir x,int k, int &ev);
void afisare(sir x, int k);
int main()
{
printf(" Generarea combinarilor de n luate cate p \n");
cnp=0;
printf(" Numarul de obiecte n = ");
scanf("%d",&n);
printf(" Tastati obiectele \n");
for(i=1;i<=n;i++)
{
printf(" a [ %d ] = ",i);
scanf("%d",&a[i]);
}
printf(" Combinari luate cate p = ");
scanf("%d",&p);
if(p<=n)
{ k=1;
x[k]=0;
while(k>0)

306
Algoritmică şi programare

{ do
{succesor(x,k,as);
if(as)
validare(x,k,ev);
}while(as && !ev);
if(as)
if(k==p) {cnp++;
afisare(x,k);
} else
{ k=k+1;
x[k]=0;
} else
k=k-1;
}
printf(" Numarul combinarilor de %d obiecte luate cate %d este egal
cu %d \n",n,p,cnp);
getch();
}
else
printf(" Eroare in date: p>n \n");
getch();
}
void succesor(sir x,int k,int &as)
{
if(x[k]<n)
{
as=1;
x[k]=x[k]+1;
}
else
as=0;
}

void validare(sir x,int k, int &ev)


{
ev=1;
if((k>=2) && !(a[x[k]]>a[x[k-1]]))
ev=0;
}

void afisare(sir x, int k)


{

307
Algoritmică şi programare

int i;
printf(" %d ( ",cnp);
for(i=1;i<=k-1;i++)
printf("%d, ",a[x[i]]);
printf("%d ) \n",a[x[i]]);
}

308
Algoritmică şi programare

Bibliografie
[1] Ariton V., Fundamente ale tehnologiei informaţiilor şi comunicaţiilor,
Ed. Didactică şi Pedagogică, 2003;
[2] Viorel A., Gheorghe P., Postolache F., Răileanu A.B., Bazele
informaticii (curs id sem II), Editura Universitară Danubius, 2010, ISBN
978-606-533-080-1;
[3] Postolache F., Ariton V., Informatică economică, Editura Universitară
Danubius, 2013, ISBN 978-606-533-319-2;
[4] Băutu A., Vasiliu P., Bazele programării calculatoarelor, Ed.
A.N.M.B., Constanţa, 2009;
[5] Carlisle M., Introduction to programming course handouts, 2010;
[6] Dragomir F.L., Postolache F., Alexandrescu G., Tools for Hierarchical
Security Modeling - The 14th International Scientific Conference
"eLearning and Software for Education", Bucharest: "Carol I" National
Defence University. (eLSE 2018), April 19 - 20, 2018, Publisher:
Advanced Distributed Learning Association, Volume 4|DOI:
10.12753/2066-026X-18-219|Pages: 34-38
[7] Dragomir F.L., Postolache F., Online Collaboration and Learning
Environment IT Modelling, The 13th International Scientific Conference
"eLearning and Software for Education", Bucharest: "Carol I" National
Defence University. (eLSE 2017), April 27 - 28, 2017, Publisher:
Advanced Distributed Learning Association, Volume 1|DOI:
10.12753/2066-026X-17-056|Pages: 382-386.
[8] Kris Jamsa & Lars Klander, Totul despre C şi C++, Ed. Teora, 2000;
[9] Postolache F, Introducere în ştiința calculatoarelor, Editura Zigotto,
2013, ISBN 978-606-669-092-8;
[10] Schildt H., C++ manual complet, Ed. Teora, 1997;
[11] http://raptor.martincarlisle.com/

309

View publication stats

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