+ All Categories
Home > Documents > Programarea Calculatoarelor -...

Programarea Calculatoarelor -...

Date post: 03-Mar-2021
Category:
Upload: others
View: 82 times
Download: 0 times
Share this document with a friend
93
Programarea Calculatoarelor Versiune 0.32 Cristian Vicas , nov. 16, 2020
Transcript
Page 1: Programarea Calculatoarelor - utcluj.rousers.utcluj.ro/~visoft/teach/programareacalculatoarelor.pdfProgramarea Calculatoarelor, Versiune 0.32 1.2Desfas˘, urare 1.2.1Abstractizare

Programarea CalculatoarelorVersiune 0.32

Cristian Vicas,

nov. 16, 2020

Page 2: Programarea Calculatoarelor - utcluj.rousers.utcluj.ro/~visoft/teach/programareacalculatoarelor.pdfProgramarea Calculatoarelor, Versiune 0.32 1.2Desfas˘, urare 1.2.1Abstractizare
Page 3: Programarea Calculatoarelor - utcluj.rousers.utcluj.ro/~visoft/teach/programareacalculatoarelor.pdfProgramarea Calculatoarelor, Versiune 0.32 1.2Desfas˘, urare 1.2.1Abstractizare

Contents

1 Introducere 11.1 Despre ce e vorba aici? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1

1.1.1 Ce este software-ul? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11.1.2 Cum e alcatuit un calculator? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2

1.2 Desfas, urare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31.2.1 Abstractizare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31.2.2 Lean . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31.2.3 CE vs CUM . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41.2.4 Cheia succesului . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5

2 Iterat, ia 1 72.1 Introducere . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72.2 Un program simplu . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8

2.2.1 Anatomia unui program . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82.3 Instruct,iuni de control . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9

2.3.1 Instruct,iunea for . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92.3.2 Instruct,iunea if . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10

2.4 Instruct,iuni, expresii s, i funct,ii . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 122.4.1 Instruct,iunile . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 122.4.2 Funct,iile . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 122.4.3 Expresiile . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13

2.5 Memoria si variabilele . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 132.5.1 Vizibilitatea unei variabile . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 152.5.2 Alte observat,ii . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 152.5.3 Init,ializarea variabilelor s, i constante . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16

2.6 Intrari s, i ies, iri . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 162.6.1 Ies, irea în consola text . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 162.6.2 Citirea de la tastatura . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17

2.7 Lean . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 192.7.1 Abordarea standard . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 192.7.2 Abordarea când învat,at,i . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 192.7.3 Abordarea când stapânit,i instrumentele . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20

3 Iterat, ia 2 213.1 Introducere . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 213.2 Tipuri de memorie accesibile programului . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21

3.2.1 Variabile globale fis, ierului curent . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22

i

Page 4: Programarea Calculatoarelor - utcluj.rousers.utcluj.ro/~visoft/teach/programareacalculatoarelor.pdfProgramarea Calculatoarelor, Versiune 0.32 1.2Desfas˘, urare 1.2.1Abstractizare

3.2.2 Variabile locale funct,iei/blocului curent . . . . . . . . . . . . . . . . . . . . . . . . . . . . 223.2.3 Accesarea memoriei Heap . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 243.2.4 Declararea variabilelor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24

3.3 Variabile (date) compuse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 243.3.1 Tablouri . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 253.3.2 Structuri . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 273.3.3 S, iruri de caractere . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28

3.4 Pointeri . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 293.4.1 Declararea pointerilor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 303.4.2 Operat,ii cu pointeri . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 303.4.3 Legatura între pointeri s, i tabele . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 313.4.4 Alocarea de memorie în zona heap . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32

3.5 Expresii . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 343.5.1 Operat,ii aritmetice . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 343.5.2 Operatorul de asignare sau atribuire . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 363.5.3 Operatori de comparat,ie s, i logici . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 373.5.4 Alt,i operatori . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 393.5.5 Prioritat,ile s, i asocierea operatorilor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 393.5.6 Supraîncarcarea operatorilor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41

3.6 Funct,ii . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 413.6.1 Apel prin referint, a . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 433.6.2 Stiva . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 453.6.3 Apel recursiv . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45

3.7 Intrari/ies, iri . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 463.7.1 Deschiderea unui fis, ier . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 463.7.2 Accesul la nivel de caracter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 473.7.3 Accesul la nivel de s, ir de caractere . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 473.7.4 Accesul la nivel binar . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 483.7.5 Redirectarea I/O standard . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48

4 Probleme propuse 494.1 Introducere . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 494.2 Citire de text din fis, ier . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 494.3 Parsare text dintr-un s, ir . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 494.4 Liste de vectori . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 504.5 Operat,ii cu liste de s, iruri de cuvinte . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 504.6 Tabele dinamince . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50

5 Iterat, ia 3 515.1 Introducere . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 515.2 Toolchain-ul de compilare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51

5.2.1 Preprocesarea . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 525.3 Programarea modulara . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55

5.3.1 Compilarea s, i Link-editarea . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 565.3.2 Modulele . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57

5.4 Variabile statice s, i constante . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 585.4.1 Modificatorul const . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 585.4.2 Modificatorul static . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 585.4.3 Modificatorul extern . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59

5.5 Lucrul cu date structurate . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 595.5.1 Array-uri de pointeri . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 595.5.2 Matrice în structura . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 605.5.3 Matrici continue . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 625.5.4 S, iruri de caractere . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62

ii

Page 5: Programarea Calculatoarelor - utcluj.rousers.utcluj.ro/~visoft/teach/programareacalculatoarelor.pdfProgramarea Calculatoarelor, Versiune 0.32 1.2Desfas˘, urare 1.2.1Abstractizare

5.5.5 Structuri recursive . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 645.5.6 Reuniuni de date s, i structuri pe bit,i . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65

5.6 Functii . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 685.6.1 Recursivitatea . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 685.6.2 Pointerii la funct,ii . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70

5.7 Strategii de programare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 735.7.1 Optimizari s, i "mai optim" . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 735.7.2 Cod curat . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73

6 Limbajul s, i mediul Python 776.1 Introducere . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 776.2 Elemente de sintaxa . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 786.3 Structuri de date . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79

6.3.1 S, ir de caractere . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 796.3.2 Liste . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 796.3.3 Tuple . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 806.3.4 Dict,ionare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 806.3.5 Funct,ii, clase, obiecte . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80

6.4 Biblioteca matematica . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 806.5 Setarea unui toolchain . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81

7 Limbajul s, i mediul MATLAB 83

8 Planificare curs 85

9 Bibliografie 87

iii

Page 6: Programarea Calculatoarelor - utcluj.rousers.utcluj.ro/~visoft/teach/programareacalculatoarelor.pdfProgramarea Calculatoarelor, Versiune 0.32 1.2Desfas˘, urare 1.2.1Abstractizare

iv

Page 7: Programarea Calculatoarelor - utcluj.rousers.utcluj.ro/~visoft/teach/programareacalculatoarelor.pdfProgramarea Calculatoarelor, Versiune 0.32 1.2Desfas˘, urare 1.2.1Abstractizare

CHAPTER 1

Introducere

1.1 Despre ce e vorba aici?

Bine at,i venit la materia Programarea calculatoarelor!

Toate dispozitivele electronice smart din jurul nostru sunt controlate de software. De la smartphone s, i smartwatchesla dispozitive integrate ce controleaza motorul de la mas, ina.

A, s, i sa nu uitam, fac rachetele sa aterizeze pe platforme mobile în mijlocul oceanului.1

Pe scurt, software-ul pune în mis, care s, i da viat, a tuturor dispozitivelor lumii moderne. Foarte put,ine dispozitive suntconstruite strict din hardware.

La aceasta materie acumulam nis, te cunos, tint,e elementare de cum sa facem calculatorul sa faca ceea ce vrem noi. Vomînvat,a cum sa folosim un limbaj de programare s, i cum sa abordam rezolvarea unei probleme de software.

Cursul acesta presupune ca NU avet,i cunos, tint,e prealabile de C. Nu va speriat,i, daca nu at,i facut la liceu programare.Pentru cei care "s, tit,i" deja C++: Not,iunile predate merg dincolo de nivelul predat la liceu. Nu ignorat,i materia doardin cauza ca at,i fost olimpici! S-ar putea sa avet,i surprize neplacute.

1.1.1 Ce este software-ul?

Procesorul (nucleul, "creierul" calculatorului) executa nis, te instruct,iuni aflate în memoria de lucru (RAM). Acesteinstruct,iuni sunt stricte, precise s, i deterministe. Ele sunt codate ca s, i un pattern (s, ablon, model) de bit,i, adica numerebinare: 00101001, 11100011 etc etc. Acest pattern de bit,i este recunoscut s, i executat de procesor. Pattern-ul de bit,ise numes, te cod mas, ina s, i alcatuies, te un program. Tot în memorie se pastreaza, tot ca pattern-uri de bit,i, s, i valorinumerice utile (datele programului).

În trecut, inginerii perforau acest pattern pe nis, te hârtii speciale s, i le încarcau în memorie. Între timp au mai evoluatun pic lucrurile. Folosim un compilator care sa traduca un text (nu chiar ca s, i cel de aici) în cod mas, ina. Acel text,înt,eles de compilator se numes, te limbaj de programare.

Aici vom învat,a sa programam calculatorul în principal în limbajul C. Daca avem timp, ne uitam s, i la alte doua limbajefoarte utile în cariera de Inginer, Python s, i MATLAB.

1 Canalul YouTube a celor de la SpaceX: https://www.youtube.com/watch?v=4jEz03Z8azc

1

Page 8: Programarea Calculatoarelor - utcluj.rousers.utcluj.ro/~visoft/teach/programareacalculatoarelor.pdfProgramarea Calculatoarelor, Versiune 0.32 1.2Desfas˘, urare 1.2.1Abstractizare

Programarea Calculatoarelor, Versiune 0.32

1.1.2 Cum e alcatuit un calculator?

Elementele principale sunt:

• Procesorul

• Memoria de lucru (RAM)

• Interfet,e cu perifericele (I/O)

• Magistralele de comunicare (Bus-urle) de pe placa de baza

Putem avea toate aceste componente puse pe o placa de baza, (ex ca la un desktop sau laptop) sau sa le avem pe toateîntr-un singur circuit. As, a numitul System On a Chip (SoC). Raspberry Pi sau Arduino, sunt doua platforme foartepopulare.

Memoria cont,ine date s, i instruct,iuni s, i este accesata în principal de procesor prin intermediul magistralelor. Circuiteleperiferice pot s, i ele interact,iona cu procesorul citind valori din mediul extern (intrare) sau producând efecte (ies, ire,output). Cele mai cunoscute dispozitive de intrare (input): Tastatura si mouse-ul. Simplist, procesorul cites, te dacaavem apasat vreun buton la tastatura s, i daca da, îl memoreaza. Ca ies, ire, avem bineînt,eles ecranul. Simplificat, fiecareLED de pe ecran, este aprins s, i stins de procesor în funct,ie de anumite reguli.2

În procesor gasim urmatoarele tipuri de instruct,iuni (din nou, foarte simplificat):

• Citire din memorie (sau de la periferice)

• Calcule aritmetice/matematice

• Scriere în memorie (sau la periferice)

• Control al fluxului de execut,ie (Care este urmatoarea instruct,iune care se va executa?)

Cu aceste patru tipuri de instruct,iuni, putem sa facem aproape orice. De la a aprinde un LED pâna la mas, ini autonome.

Un exemplu de (pseudo)cod ce aduna doua numere s, i salveaza rezultatul:

Listing 1.1: Operat,ie de adunare scrisa într-un pseudo limbaj deasamblare.

mov r0, [10340]mov r1, [10344]add r2, r0, r1mov [10348], r2

Adica, ia cont,inutul de la adresa de memorie 10340 si 10344 s, i le stocheaza în regis, trii r0 s, i r1. Aduna s, i pastreazarezultatul în r2. Apoi, scrie rezultatul la adresa 10348. Simplu? Dar laborios. Pana la urma nu face altceva decâtechivalentul matematic al lui c = a + b unde a s, i b sunt cunoscute.

Pentru a abstractiza s, i us, ura lucrul cu procesorul, s-au inventat limbajele de programare.

Limbajul C este suficient de flexibil si portabil sa funct,ioneze (aproape) identic pe multe platforme s, i sisteme deoperare.

2 În realitate procesorul nu interogheaza fiecare periferic s, i nici nu controleaza constant fiecare LED din ecran. La Arhitectura Calculatoarelorvet,i învat,a mult mai în detaliu despre întreruperi, DMA, buffer-e, controller-e s, i alte concepte. DAR pentru simplificare, deocamdata, acceptat,iprezentarea de mai înainte.

2 Chapter 1. Introducere

Page 9: Programarea Calculatoarelor - utcluj.rousers.utcluj.ro/~visoft/teach/programareacalculatoarelor.pdfProgramarea Calculatoarelor, Versiune 0.32 1.2Desfas˘, urare 1.2.1Abstractizare

Programarea Calculatoarelor, Versiune 0.32

1.2 Desfas, urare

1.2.1 Abstractizare

Vrem sa mergem într-o excursie, cu scopul de a vizita un oras, nou. Dintr-o alta t,ara. Presupunem ca nu este 2020 s, ica anii urmatori sunt mai veseli.

Am fixat destinat,ia, am fixat s, i scopul vizitei, acum mai trebuie sa organizam. Cu ce începem? Putem lucra latransportul pâna la destinat,ie, cazare în oras, ul dorit, actele s, i permisele necesare, lista de necesitat,i, bagaje, etc.

SAU

Putem planifica în detaliu ce vom îmbraca în fiecare zi (înca nu s, tim cât stam), ce vom servi la micul dejun (undestam?) s, i la un moment dat sa ajungem s, i la transport sau cazare.

Evident, abordarea init,iala are mai multe s, anse de succes. Oare este posibil sa ne deplasam acolo? Ce acte trebuie?Putem începe apoi cu o cautare dupa un mijloc de transprt. Am gasit ceva decent, putem cauta cazare în intervaluldorit. Poate ca, cazarea va impune nis, te restrict,ii mai puternice (ex. trebuie stat o saptamâna întreaga). Putem avansas, i rezerva cazarea, dupa care sa avansam s, i sa rezervam transportul.

Acum ca linile "mari" ale calatoriei sunt fixate putem adauga detalii. Cum mergem pâna la aeroport/gara? Cummergem de la aeroportul destinat,ie la cazare? Oare cum voi fi îmbracat?

Mai sus am prezentat o descompunere a problemei de sus în jos. La fiecare etapa am concretizat anumite elemente (egtransportul) pe când altele au ramas abstracte (voi fi îmbracat cumva s, i voi avea bagaje)

Capacitatea de a rat,iona abstract cu diverse concepte este foarte importanta pentru un Inginer.

1.2.2 Lean

În trecut, o sarcina mare, se diviza în o serie de etape succesive:

• Specificarea cerint,elor problemei

• Analiza în detaliu a cerint,elor

• Proiectarea solut,iei

• Executarea, implementarea proiectului

• Testarea

• Operat,ionalizarea s, i livrarea la client.

Aceasta metodologie s-a dovedit utila în anumite cazuri de nis, a, dar, în IT, este fatala. De obicei este foarte beneifcapentru manageri, consultant,i, subcontractant,i dar în nici un caz benefica pentru clientul final sau echipa de inginericare realizeaza sistemul.

Apocrific pâna s, i Winston Royce, presupusul autor al metodologiei3 a insistat mult timp asupra gres, elii facute demanagement. Prea târziu, banii curgeau, proiectele se întârzâiau, toata lumea care avea putere de decizie, era fericita!Mai put,in clientul.

În acest curs vom folosi abordarea lean. Sau iterativ. Vom urmari un obiectiv s, i vom ajunge la obiectiv facând câtevaiterat,ii. Fiecare iterat,ie va avea o finalitate, avansând (sau rezolvând) un element cheie al problemei s, i în acelas, i timprafinând solut,ia anterioara. Aceasta abordare (numita Lean) s-a dovedit deosebit de eficienta în realizarea sistemelortehnice s, i comperciale complexe.

3 Articolul Managing the development of large software systems poate fi gasit deocamdata aici: http://www-scf.usc.edu/~csci201/lectures/Lecture11/royce1970.pdf

1.2. Desfas, urare 3

Page 10: Programarea Calculatoarelor - utcluj.rousers.utcluj.ro/~visoft/teach/programareacalculatoarelor.pdfProgramarea Calculatoarelor, Versiune 0.32 1.2Desfas˘, urare 1.2.1Abstractizare

Programarea Calculatoarelor, Versiune 0.32

Metodologia lean se regases, te în machine learning ("Artificial Intelligence"), în business s, i în tot ceea ce înseamnaindustria software de azi (La software lucreaza împreuna cu metodologia agile).

Vom învat,a C-ul iterativ, prima iterat,ie va va da rapid cunos, tint,e elementare despre majoritatea elementelor.Urmatoarele, vor aprofunda not,iunile s, i va vor învat,a noi s, i noi instrumente din ce în ce mai complexe.

Problemele le vom rezolva tot iterativ. Scriem un cod simplu, care poate rezolva un subpunct mai simplu, compilam,executam, observam rezultatul. Apoi, daca totul este ok, trecem la urmatorul subpunct. Scriem cod care credemca rezolva subpunctul, compilam, rulam s, i repetam daca nu obt,inem rezultatul dorit. În urmatoarele iterat,ii putemde exemplu rafina cele doua subpuncte s, i rezolva alte cerint,e (exemplu, elegant,a codului). De obicei regula este caiterat,iile sa fie cât mai scurte.

Avantajele acestei abordari este ca la finalul fiecarei iterat,ii avet,i o solut,ie part,iala a problemei. Acest lucru este foarteutil in business (se poate livra ceva clientului) dar s, i la examen! Daca se termina timpul înainte de a rezolva toataproblema, avet,i nis, te subpuncte rezolvate!

De obicei, în cadrul iterat,iei avem cele câteva faze de identificare a cerint,elor, proiectare, implementare s, i testare. Darele se axeaza strict pe subproblema pe care o avem ca t,inta în iterat,ia curenta.

Atent,ie, abordarea top-down s, i lean sunt COMPATIBILE! Abstractizarea ne permite sa alegem o subproblema (ex. neconcentram pe cazarea la destinat,ie) în timp ce alte probleme sunt abstractizate (s, tim ca putem duce bagaje s, i ca existaposibilitat,i de transport)

Abordarea iterativa ne permite sa avansam cu rezolvarea problemei în timp ce exista mai multe constrângeri ceinteract,ioneaza. (ex cazarea depinde de transport iar datele transportului depind de cazare).

Memoria umana are capacitate limitata. Este greu sa analizezi 500 de pagini de cerint,e. Specificat,iile unui proiect potfi incomplete s, i vagi. Mai mult, de obicei se s, i schimba pe parcursul proiectului. Toate aceste limitari sunt adresatefrumos s, i elegant de abordarea lean

1.2.3 CE vs CUM

Când abordam o problema noua exista câteva întrebari.

CE:

• Ce trebuie sa rezolvam?

• Ce trebuie sa livram clientului?

• Ce constrângeri avem?

• Ce e foarte important? La ce nu trebuie sa dam atent,ie?

CUM:

• Cum sa implementez solut,ia?

• Cum sa aprind LED-u?

• Cum sa citesc de la tastatura?

Întrebarile de tip CE sunt întrebari de nivel înalt care ne ajuta sa descompunem problema în bucat,i mai mici.Odata descompusa, putem trece la CUM. Aceste întrebari ne ajuta sa implementam cod care rezolva fiecare bucatadescompusa.

Partea de limbaj C a cursului se axeaza pe întrebari de tip CUM. Mare atent,ie, din când în când abordam si CE-u. Unprogramator mediocru are partea de implementare foarte bine pusa la punct (s, tie rapid sa raspunda cu solut,ia potrivitala întrebarile de tip CUM). Un Inginer bun, stapânes, te si CE-u. Din pacate, arta de a pune întrebari bune se câs, tigaodata cu experient,a.

4 Chapter 1. Introducere

Page 11: Programarea Calculatoarelor - utcluj.rousers.utcluj.ro/~visoft/teach/programareacalculatoarelor.pdfProgramarea Calculatoarelor, Versiune 0.32 1.2Desfas˘, urare 1.2.1Abstractizare

Programarea Calculatoarelor, Versiune 0.32

Evident exista o ierarhie aici, ca s, i la abstractizare. De exemplu: Trebuie sa semnalizez un pericol. CUM fac asta?Aprinzand s, i stingând periodic un bec. CE instrumente sunt cele mai potrivite? Ce constrângeri am? (Ex. autonomie,mediu agresiv, praf, umezeala, etc) CUM aprind un LED? CUM sting LED-ul? etc etc.

Presupunem ca at,i rezolvat problema de mai sus. Acum trebuie sa realizat,i o mas, inarie care efectueaza o operat,iepericuloasa s, i trebuie sa semnalizat,i acest lucru (CE-ul).

Cum o facet,i? Cu metoda de mai sus. Odata ce at,i ajuns la acest CE (trebuie sa semnalizez) va putet,i opri cudescompunerea pentru ca avet,i solut,ia. Acest concept se numes, te abstractizare. Abstractizam operat,ia de "avertizare"pentru ca este bine cunoscuta.

La fel s, i la programare, pe masura ce învat,at,i concepte, le grupam în entitat,i abstracte pe care le vom folosi mereu.

1.2.4 Cheia succesului

Un student bun, în urma acestui curs va stapâni foarte bine instrumentele limbajului de programare s, i va fi capabilsa descompuna o problema complexa în bucat,i rezolvabile. Prima parte (cunoas, terea instrumentelor) vine odata cupractica. Partea a doua, (abilitat,ile de rezolvare a problemelor) vine cu s, i mai multa practica.

Nu va lasat,i pacalit,i glumele, cu diverse cantitat,i de sare, prezente aici. Materialul didactic este dens. Fiecare paragrfeste o definit,ie, descrie o proprietate sau arata o observat,ie importanta. Multe concepte odata enunt,ate se presupuncunoscute s, i înt, elese.

Munca alocata pentru aceasta specializare s, i efortul depus de voi, pentru a va însus, i not,iunile, depas, es, te timpul fizicalocat în cadrul orarului (în persoana sau online). Va încurajez sa muncit,i s, i sa studiat,i în echipe. Sa colaborat,i atuncicând învat,at,i. Folosit,i din plin mijloacele de comunicare avute la dispozit,ie atunci când avet,i dificultat,i. Dar atent,ie,colaborarea la evaluari este frauda s, i se pedepses, te sever.

Va recomand sa facet,i pauza când învat,at,i. Concentrat,i-va 20-30 de minute dupa care 5-10 minute pauza. Reulat,i(mai ales materialul video) pâna înt,eleget,i s, i experimentat,i cu codul s, i conceptele învat,ate aici.

Când dat,i de greu, când "nu va iese ceva" folosit,i regula de 15 minute:5 Încercat,i sa rezolvat,i singuri problema pentru15 minute (folosind documentat,ie, curs, google, etc) s, i daca nu reus, it,i, escaladat,i. Ceret,i ajutor de la colegi, asistent,i,profesori.

Orice at,i face, NU ÎNVAT, AT, I PE DE ROST!

Romengleza este din pacate din ce în ce mai prezenta în lumea IT-ului as, a ca, get used to it!

Important: Materialul video atas, at cursului este adit,ional materialului scris. În materialul video conceptele suntexplicate mai pe larg. Deocamdata doar câteva arii sunt acoperite. Baza s, i referint,a dupa care învat,at,i s, i suntet,ievaluat,i ramâne suportul de curs s, i îndrumatorul de laborator.

5 https://twitter.com/math_rachel/status/764931533383749632

1.2. Desfas, urare 5

Page 12: Programarea Calculatoarelor - utcluj.rousers.utcluj.ro/~visoft/teach/programareacalculatoarelor.pdfProgramarea Calculatoarelor, Versiune 0.32 1.2Desfas˘, urare 1.2.1Abstractizare

Programarea Calculatoarelor, Versiune 0.32

6 Chapter 1. Introducere

Page 13: Programarea Calculatoarelor - utcluj.rousers.utcluj.ro/~visoft/teach/programareacalculatoarelor.pdfProgramarea Calculatoarelor, Versiune 0.32 1.2Desfas˘, urare 1.2.1Abstractizare

CHAPTER 2

Iterat, ia 1

2.1 Introducere

În iterat,ia curenta vet,i învat,a sa compunet,i un program simplu. Vom merge orizontal, adica vom atinge not,iuni multe,fara a intra în detalii. Multe concepte le vom abstractiza.

Recaptiulând put,in. S, tim ca procesorul ia instruct,iuni din memorie s, i le executa una dupa cealalta (secvent,ial). Tot înmemorie se pot stoca s, i diverse valori numerice, utile noua.

Instruct,iunile în cod mas, ina nu sunt foarte us, or de citit sau de scris. Folosim un limbaj de programare, limbaj din careun compilator va crea acest cod mas, ina executabil.

În introducere am vazut un exemplu de "cod mas, ina"1 unde procesorul executa secvent,ial fiecare instruct,iune.Limbajul C este parte din paradigma programarii imperative. În aceste limbaje îi spun procesorului pas cu pas cesa execute. (C, C++, Java, Python, Matlab, Pascal ar fi câteva exemple). Din paradigma imperativa, procedurala sedesprinde programarea orientata pe obiect, POO (C++, Java, Python).

Mai exista s, i alte paradigme, programarea funct,ionala fiind utila mai ales în sisteme distribuite (Scala, F#, Elixirfiind câteva limbaje des folosite). Programarea logica este mai put,in folosita în industrie, dar are anumite observat,iiteoretice vitale în procesarea paralela de performant, a înalta (High Performance Computing). Fiecare paradigma areavantajele ei, as, a ca sa nu va mire prezent,a elementelor de programare funct,ionala în limbaje "imperative" ca Java,Javascript sau Python. Sub nici o forma nu trebuie sa ignorat,i aceste paradigme!

1 De fapt ce am aratat în introducere este tot un text în limbaj de asamblare, pe care un compilator îl va traduce în cifre. Dar la ora actuala numai exista mult,i ingineri care sa înt,eleaga sau sa scrie direct în cod mas, ina. Probabil Jeff Dean (Cautat,i: "Jeff Dean facts")

7

Page 14: Programarea Calculatoarelor - utcluj.rousers.utcluj.ro/~visoft/teach/programareacalculatoarelor.pdfProgramarea Calculatoarelor, Versiune 0.32 1.2Desfas˘, urare 1.2.1Abstractizare

Programarea Calculatoarelor, Versiune 0.32

2.2 Un program simplu

Primul program în cam orice limbaj de programare este celebrul "Hello world!". Ce fac aceste tipuri de programe? Deobicei afis, eaza un mesaj pe ecran. Sau fac un LED sa clipeasca3. Scopul lor este de a demonstra cea mai mica unitatefunct,ionala a unui limbaj de programare sau a unei tehnologii.

Listing 2.1: "Hello world!" în C

#include <stdio.h>int main(){

printf("Hello World!");return 0;

}

Acum s, tim cum sa afis, am un mesaj pe ecran. La laborator, vet,i învat,a despre cum sa compilat,i acest program scurt.

Oare cum putem modifica mesajul afis, at? Oare ce putem modifica, din textul de mai sus, fara sa modificamfunct,ionalitatea? Dar fara sa avem erori de compilare?

Încercat,i! Modificat,i bucat,i din cod s, i vedet,i ce se întâmpla!

2.2.1 Anatomia unui program

Partea cea mai importanta s, i care produce modificari vizibile este printf("Hello World!");. Mai mult, dacamodific textul dintre ghilimele, se modifica s, i mesajul afis, at pe ecran. Evident!

Cu except,ia spat,iilor albe, orice modificare la cod va duce la o eroare de compilare. Modificarea mesajului dintreghilimele va duce la modificarea mesajului pe ecran.

În acest moment avem un instrument! Putem afis, a ceva pe ecran!

Fiecare linie din program face ceva anume. Pe scurt:

• #include <stdio.h> îi spune compilatorului ca avem nevoie sa lucram cu I/O (input/output)

• int void main() s, i acoladele { respectiv } fac parte din limbaj

• printf("mesaj"); afis, eaza mesajul pe ecran!

• return 0 spune sistemului de operare ca totul s-a executat cum trebuie s, i nu au fost erori. Încercat,i sa returnat,ialtceva.

• Comanda de afis, are este înconjurata de { respectiv }. Aceste perechi de acolade definesc un bloc de instruct, iuni

Simplu? Deocamdata, da! Vom vedea mai încolo ca ceea ce denumim noi compilator e de fapt un lant, de instrumente.Dar, abstractizam întreg lant,ul (numit toolchain) sub denumirea simpla de compilator.

Atent,ie, am introdus conceptul de bloc de instruct, iuni.3 https://www.arduino.cc/en/Tutorial-0007/BlinkingLED

8 Chapter 2. Iterat, ia 1

Page 15: Programarea Calculatoarelor - utcluj.rousers.utcluj.ro/~visoft/teach/programareacalculatoarelor.pdfProgramarea Calculatoarelor, Versiune 0.32 1.2Desfas˘, urare 1.2.1Abstractizare

Programarea Calculatoarelor, Versiune 0.32

2.3 Instruct, iuni de control

2.3.1 Instruct, iunea for

Problema: Vreau sa afis, ez primele numere pare non negative mai mici ca 10.

Deja avem câteva instrumente! Un pic de inventar:

• Compilatorul, ia un fis, ier text s, i îl convertes, te în cod mas, ina executabil

• Instruct,iuni în limbajul C, (bucat,i de text) cu care pot face calculatorul sa afis, eze un mesaj pe ecran!

Oare putet,i rezolva problema? Încercat,i! Iterativ! Mai întâi o cifra, apoi compilat,i. Apoi a 2-a cifra para, compilat,i.Apoi toate! A fost greu? Dar oare se poate mai elegant? Observat,i codul de mai jos:

Listing 2.2: Instruct,iunea for

1 #include <stdio.h>2 int main()3 {4 for(int i = 0; i < 10; i = i + 2){5 printf("%d ", i);6 }7 return 0;8 }

Hmm, parca solut,ia în care modificam doar linia cu printf arata mai bine s, i mai simplu! Oare ce avantaje areaceasta solut,ie?

Începem sa vedem câteva elemente care se suprapun cu cerint,ele problemei. Avem instruct,iunea de afis, are pe ecran,avem cifra 10, avem o operat,ie de incrementare din doi în doi, avem o operat,ie de setare la zero.

Descris în cuvinte, am dat aceste instruct,iuni procesorului:

• Rezerva o zona de memorie s, i denumes, te-o i. Pune acolo numarul 0 int i = 0

• Afis, eaza pe ecran ceea ce se afla în zona de memorie i: printf("%d ", i)

• Adauga la valoarea din zona de memorie i, numarul 2. Scrie rezultatul înapoi la i. i = i + 2

• Repeta, cât timp la locat,ia i, valoarea este mai mica cu 10. i < 10

Din nou, t,inet,i cont de abstractizare! Volumul de instruct,iuni în cod mas, ina este mult mai mare decât ce am scris noi.Din fericire NU avem treaba cu codul mas, ina. Deasemenea, construct,ia din linia 4 s-ar putea sa va sperie un pic. Ovom "sparge" în unitat,i mai mici imediat s, i vom vedea care part,i pot fi modificate ca sa ne serveasca noua.

În codul de mai sus am introdus discret s, i lucrul cu memoria. S, tim ca principala sarcina a procesorului este sa"socoteasca" cu datele stocate în memorie. Cu ajutorul variabilei i, abstractizam conceptul de memorie s, i operat,iilecare se fac cu memoria.

Bun, haidet,i sa vedem daca at,i capatat intuit,ie despre cum funct,ioneaza for-ul. Modificat,i codul de mai sus sa afis, ezenumerele pare mai mici de 20. Mai mici de 200!

Ati reus, it? Cât de mare a fost schimbarea? Dar daca ramâneam la solut,ia anterioara?

Am scris mai mult cod, cu instruct,iuni noi s, i complicate dar am câs, tigat multa flexibilitate!

Instruct,iunea for permite controlul execut,iei programului. Instruct,iunea determina executarea repetata a ceea ce seafla în interiorul acoladelor imediat urmatoare (blocul de instruct,iuni).

În interiorul instruct,iunii gasim nis, te operat,ii. Acestea controleaza de câte ori se executa blocul.

Sa ne mai uitam odata la instuct,iunea for:

2.3. Instruct, iuni de control 9

Page 16: Programarea Calculatoarelor - utcluj.rousers.utcluj.ro/~visoft/teach/programareacalculatoarelor.pdfProgramarea Calculatoarelor, Versiune 0.32 1.2Desfas˘, urare 1.2.1Abstractizare

Programarea Calculatoarelor, Versiune 0.32

for( A ; B ; C ) instructiune

A, B s, i C sunt expresii. (Nu intram înca în definit,ia expresiei, gândit,i-va ca la o expresie matematica)

Expresia A va fi executata înainte de a intra în bucla. Ea are rolul de a init,ializa ciclul for.

Expresia B este condit,ia de continuare. Ciclul for se va executa cât timp aceasta expresie este adevarata. În limbajulC, o expresie este adevarata, True, daca are valoare diferita de zero. Atent,ie, prima evaluare a expresiei se face înaintede a intra in ciclu. Asta înseamna ca sunt cazuri în care ciclul for nu se va executa deloc. Din cauza ca schimbareavalorii lui B duce la terminarea ciclului, i se mai zice lui B s, i condit,ia de terminare.

Daca expresia B este adevarata (True) se va executa instruct,iunea (sau blocul de instruct, iuni) instructiune.

Expresia C este apelata de fiecare data dupa ce s-a executat un pas al ciclului for. Este numita s, i expresia dereinit,ializare. Atent,ie, la final, se va executa ultima iterat,ie a ciclului for, se va evalua C s, i apoi se va evalua B.Dupa care, programul continua cu urmatoarea instruct,iune de dupa for.

Întrebare: Ce valoare are memorata variabila i dupa ce se iese din ciclul for? Nu va dat,i seama dupa regulile de maisus? Nu e nimic! Adaugt,i o instruct,iune de afis, are, dupa ies, ira din ciclul for. În funct,ie de compilator, s-ar putea sa vadea erori cum ca identificatorul i nu este declarat.

Modificam un pic codul:

#include <stdio.h>void main(){

int i;for(i = 0; i < 10; i = i + 2){printf("%d ", i);

}printf("Dupa for, i = %d ", i);

}

Vedem ca variabila i cont,ine valoarea 10. Explicabil, dupa ce se executa afis, area cu valoarea 8, bucla for va executaexpresia C, care va face ca i sa ia valoarea 10. Apoi, înainte de a re-intra în ciclu se verifica B. Aceasta va fi falsapentru ca în acest pas, i NU este strict mai mic decat 10. Este egal cu 10.

Vom reveni la eroarea care a aparut când textul int i era prezent în interiorul instruct,iunii for. T, ine de vizibilitateavariabilelor în interiorul codului s, i de diverse variat,ii ale implementarii compilatoarelor.

2.3.2 Instruct, iunea if

Am rezolvat problema anterioara apelând la cunos, tint,ele noastre de matematica. S, tim ca primul numar par este 0, s, timca ele urmeaza o progresie aritmetica cu pasul 2. As, a ca generarea lor, matematic, a fost banal.

Ce ne facem însa daca, nu putem da o solut,ie matematica? Daca de exemplu, trebuie sa afis, am radacinile unei ecuat,iicu sinus, logaritm, exponent,iale, x ridicat la puteri? Presupunând doar ca accepta undeva, o radacina întreaga înintervalul cautat. S, i ca vrem sa gasim o solut,ie algoritmica la aceasta problema.

CE trebuie sa facem? Iteram fiecare valoare întreaga posibila pe interval, s, i verificam funct,ia noastra grea. Afis, amdoar când rezultatul este zero. Simplu? Daca e sa aprofundam problema, s, tim CUM sa facem iterat,ia, s, tim CUMsa afis, am valoarea pe ecran, ce instrument nu avem înca este acela în care controlam fluxul de execut,ie al codului înfunct,ie de o condit,ie. Aici vine instruct,iunea if.

Repetam problema cu numerele pare s, i nu implementam funct,ii matematice grele, deocamdata. Însa, rescriem, ca safolosim instruct,iunea if.

CE trebuie sa facem? Trebuie sa parcurgem toate numerele întregi de la 0 la 10 s, i pentru fiecare numar trebuie sadecidem daca numarul este par sau nu. Daca este par, îl afis, am.

10 Chapter 2. Iterat, ia 1

Page 17: Programarea Calculatoarelor - utcluj.rousers.utcluj.ro/~visoft/teach/programareacalculatoarelor.pdfProgramarea Calculatoarelor, Versiune 0.32 1.2Desfas˘, urare 1.2.1Abstractizare

Programarea Calculatoarelor, Versiune 0.32

Matematic, verificarea paritat,ii se face cu modulo. In limbajul C, operatorul modulo este %. Da, l-am mai vazut îninterior la printf. Acolo are cu totul alt rol. Din pacate sunt multe astfel de elemente în limbajul C. Stat,i sa ajungemla paranteze () sau la *. Operatorul * pica cei mai mult,i student,i la examen!

12 modulo 2 este echivalent cu 12 % 2 în C. Rezultatul este 0. Adica, restul împart,irii lui 12 cu 2 este 0. Adica12 este numar par. Bingo! Am rezolvat o parte din problema! Ne oprim aici? NU!

Abordam iterativ problema, as, a ca va recomand sa modificat,i codul astfel:

• Facet,i ca valoarea lui i sa mearga de la 0 la 10 din 1 în 1. Compilat,i, rulat,i, corectat,i daca sunt gres, eli.

• Afis, at,i valoarea lui i s, i valoarea lui i % 2. Comanda printf("%d %d ", i, i % 2); ar trebui safunct,ioneze.

Vedem ca pentru numerele pare, i%2 este zero.

Acum putem itera din nou s, i face codul mai elegant. Introducem formal instruct,iunea if :

if( condit, ie ){

cod A

}else{

cod F

}

Daca condit, ie se evalueaza la o valoare diferita de zero, se va executa cod A. Daca se evalueaza ca fals, se va executacod F. Asta înseamna ca bucata cod A se va executa doar când condit,ia este adevarata. Partea cu else poate salipseasca.

Convent,ia din limbajul C, este ca, daca o zona de memorie cont,ine doar 0, variabila respectiva se considera a aveavaloarea de adevar, Fals. Daca exista un pattern diferit de zero, valoarea de adevar se considera a fi Adevarat.

Mai introducem o expresie. Daca vreau sa aflu ca doua variabile sunt identice, folosesc operatorul ==. Expresia a ==b va fi diferita de zero (Adevarata) daca în zona de memorie a lui a s, i b se afla acelas, i pattern de bit,i. (Observat,ievitala pentru a înt,ege mai încolo operat,iile cu pointeri. Mai citit,i odata textul, observând expresia acelas, i pattern debit,i).

Acum putem trece la a raspunde întrebarilor CUM. Cum verific daca un numar este par? i % 2 == 0 CUMcontrolez afis, area, astfel încât sa se afis, eze doar numerele pare? Cu if.

Punet,i pauza s, i încercat,i sa rezolvat,i problema singuri.

Mai jos, o solut,ie posibila.

Listing 2.3: Numere pare, cu if

#include <stdio.h>void main(){

int i;for(i = 0; i < 10; i = i + 1){if (i % 2 == 0){

printf("%d ", i);}

}}

O gres, eala frecventa care se face este aceea de a uita un = în expresia de comparat,ie. Adica în loc de a == b sascriem a = b. Dezastru total. Valoarea expresiei a = b este valoarea lui b. As, a ca daca vrem sa verificam dacai este 5, scriind gres, it if ( i = 5), partea cod A va fi executata tot timpul! Pentru ca valoarea 5 este adevarata,conform convent,iei din limbajul C.

2.3. Instruct, iuni de control 11

Page 18: Programarea Calculatoarelor - utcluj.rousers.utcluj.ro/~visoft/teach/programareacalculatoarelor.pdfProgramarea Calculatoarelor, Versiune 0.32 1.2Desfas˘, urare 1.2.1Abstractizare

Programarea Calculatoarelor, Versiune 0.32

2.4 Instruct, iuni, expresii s, i funct, ii

Limbajul C este imperativ, fiecare instruct,iune se executa secvent,ial. Cu instruct,iunile de control putem alege care vafi blocul urmator care va fi executat. Dar în interiorul unui bloc, instruct,iunile se executa secvent,ial.

Am vazut pe scurt câteva elemente de limbaj, acum le dam o definit,ie mai riguroasa.

În limbajul C, o instruct,iune este de obicei terminata cu caracterul ;. Avem mai multe tipuri de instruct,iuni, în funct,iede ce avem înainte de acest ;.

2.4.1 Instruct, iunile

Comentariul. Putem scrie orice text, daca la începutul liniei punem doua slash-uri //. Sau putem înconjura textuldorit de perechea /* */ (poate fi în interiorul unui cod valid sau se poate întinde pe mai multe linii)

Instruct, iunea vida unde avem doar un ;.

Instruct, iunea normala: Poate fi un apel de funct,ie (vom vedea imediat), un exemplu bun ar fi linia cu printf. Totaici intra s, i instruct,iunea formata dintr-o expresie urmata de ;.

Declarat, iile de variabile le vom aprofunda la variabile. Exemplu, int i;. Observat,i ; de la sfârs, it.

Avem apoi instruct, iuni de control ale mersului programului, am vazut deja for s, i if - else. Mai sunt, while,do - while, case - switch, continue, break, etc. Majoritatea le vom vedea la laborator, ele fiind doarvariat,ii ale celor doua instruct,iuni de baza, if s, i for.

Blocul de instruct, iuni este o secvent, a de instruct,iuni prinsa între acolade {}. Atent,ie, din punct de vedere al C-uluiun bloc de instruct,iuni este vazut ca o singura instruct,iune. Asta înseamna ca peste tot unde este legal sa apara oinstruct,iune, poate aparea un bloc de instruct,iuni. S, i de regula, s, i reciproc:

int i;for(i = 0; i < 10; i = i + 1)

printf("%d ", i);printf("\nLa iesire din ciclu: %d ", i);

Foarte important, putem avea blocuri îmbricate. Am vazut la Cod 2.3 trei niveluri de îmbricare.

Instruct,iunile se pot grupa în funct,ii. Funct,iile pot accepta parametri s, i pot returna valori, cam la fel ca o funct,iematematica. Fara sa s, tim, am lucrat cu doua funct,ii. printf s, i atent,ie, main. Da, înca de la Hello World, at,i grupatinstruct,iunile într-o funct,ie. Aceasta funct,ie main are o semnificat,ie deosebita. Compilatorul cauta aceasta funct,ie capunctul principal de intrare în program. Prima linie de cod din funct,ia main (nu chiar, dar abstractizam deocamdata)este prima care se va executa în momentul în care procesorul ajunge sa execute programul nostru.

2.4.2 Funct, iile

Funct, iile nu sunt doar un mod, comod pentru noi, cu care grupam codul. Exista instruct,iuni speciale de procesor caregestioneaza apelul de funct,ie. Deocamdata nu vom simt,i diferent,a între a avea nis, te instruct,iuni grupate astfel încât sapoata fi apelate us, or s, i a avea o funct,ie cu tot ce înseamna o funct,ie. (stiva, variabile locale, recursivitate, etc).

Funct,ia printf ascunde în spatele ei un cod foarte complex care poate sa transforme un pattern de bit,i din memorieîn text clar, interpretabil de oameni (human readable). Este o funct,ie, de multe ori deja compilata la nivelul toolchain-ului.

Din acest punct vom face distinct,ie clara între o instruct,iune printf("hello"); s, i un apel de funct,ie:printf("hello"). Care e diferent,a? Caracterul ; de la sfârs, it. Diferent,a este foarte importanta pentru ca unapel de funct,ie poate sa apara într-o expresie. E perfect legal (dar nu prea are sens) sa scriem expresia i = 10 +printf("hello"). S, i sa o scriem ca s, i condit,ie la if. NU VA RECOMAND SA FACET, I ASTA!!! Dar este legal

12 Chapter 2. Iterat, ia 1

Page 19: Programarea Calculatoarelor - utcluj.rousers.utcluj.ro/~visoft/teach/programareacalculatoarelor.pdfProgramarea Calculatoarelor, Versiune 0.32 1.2Desfas˘, urare 1.2.1Abstractizare

Programarea Calculatoarelor, Versiune 0.32

din punct de vedere al C-ului. (valid din cauza ca printf returneaza ceva. Daca funct,ia apelata nu returneaza nimic,nu este valid ca apelul sa apara într-o expresie)

Atunci când apelam o funct,ie, compilatorul copiaza cont,inutul memoriei variabilelor s, i a constantelor scrise întreparantezele de la linia de unde se fae apelul, în zona de memorie a variabilelor funct,iei (acolo unde sunt instruct,iunilefunct,iei). Acest fapt este esent,ial când vet,i proiecta funct,ii.

În exemplul Cod 2.3 am vazut cum se declara o funct,ie, funct,ia main(): un cuvânt cheie urmat de nume, apoi ()urmat, atent,ie, de un bloc de instruct,iuni. În cazul nostru, numele este main, cuvântul cheie este void (acesta aratace returneaza funct,ia, în cazul nostru declaram ca nu returneaza nimic) Dupa paranteze urmeaza un bloc de instruct,iunidelimitat de acolade: {}. Nu putem pune doar o instruct,iune, ca s, i la for.

Am vazut s, i cum se apeleaza o funct,ie, cu parametri. Se scrie numele funct,iei urmat de parametrii funct,iei, întreparanteze. Prin intermediul acestor parametri, transmitem de exemplu, codului din printf ce s, i cum sa afis, eze peecran.

2.4.3 Expresiile

Expresia este o combinat,ie de operat,ii matematice, atribuiri, apeluri de funct,ii etc. care la sfârs, it are o valoare.

Exemple de operat,ii matematice: +, -, * , /, %. Aces, tia se numesc operatori. Ei "opereaza" pe operanzi. (constante,variabile, apeluri de funct,ii).

Am mai întâlnit s, i operatorul de egalitate == respectiv diferit !=. Putem avea comparat,ii de inegalitate, mai mic <,mai mare > respectiv <=, >=. Putem construi expresii logice mai complexe cu operatori logici: s, i &&, sau ||, negare!.

Ca s, i în matematica, operatorul de înmult,ire are prioritate fat, a de cel de adunare. Deocamdata ne oprim aici cuoperatorii. Sunt mult mai mult,i, îi vom atinge pe majoritatea iar prioritat,ile lor sunt un subiect care pune probleme laexamen s, i da multe bug-uri.

Mare atent,ie la operatorul de atribuire = s, i cel de comparat,ie == . Sursa inepuizabila de bug-uri!!!

2.5 Memoria si variabilele

Memoria calculatorului este vazuta de procesor ca un s, ir de octet,i. Un octet este un numar reprezentat binar prin 8 bit,i(One byte have 8 bits). Într-un octet putem memora 256 de valori, exemplu de la 0 la 255.

Zecimal Binar0 == 00000000255 == 11111111

Prima locat,ie de memorie are adresa 0 iar ultima adresa depinde de cantitatea de memorie disponibila (nu chiar, darlasam paginat,ia pentru Arhitectura Calculatoarelor).

Pentru ca e greu s, i exista un risc mare de securitate sa lucram cu pozit,ii fixe în memorie, exista conceptul de variabile.Acestea pot fi privite ca s, i un pseudonim (alias) pentru o anumita adresa. Variabilele au un tip s, i un nume.

Când scriet,i instruct,iunea care declara o variabila, de exemplu int i; facet,i câteva lucruri:

• Compilatorul s, tie ca va trebuie o zona de memorie, o rezerva. Câta memorie? Depinde de tipul variabilei.

• Peste tot unde va aparea i compilatorul va "înlocui" litera i cu ceea ce se afla stocat la adresa rezervata anterior

• Când va implica variabila i în diverse operat,ii va s, ti ca pattern-ul de bit,i de la acea adresa reprezinta un întregcu semn. Cuvântul cheie pus înaintea numelui variabilei este tipul variabilei. El specifica cum interpreteazacompilatorul pattern-ul de bit,i stocat la adresa variabilei.

2.5. Memoria si variabilele 13

Page 20: Programarea Calculatoarelor - utcluj.rousers.utcluj.ro/~visoft/teach/programareacalculatoarelor.pdfProgramarea Calculatoarelor, Versiune 0.32 1.2Desfas˘, urare 1.2.1Abstractizare

Programarea Calculatoarelor, Versiune 0.32

Toate cele trei puncte de mai sus sunt vitale s, i trebuie sa va intre foarte bine în reflexe, atunci când vedet,i o variabila.

Rezervarea memoriei este importanta pentru ca programul vostru nu este singurul care se executa pe mas, ina. Dacascriet,i aiurea în memorie (se poate) în cel mai bun caz sistemul de operare va lichideaza programul iar într-un scenariupesimist dat,i peste cap întreaga mas, ina.

Nu e foarte dragut, acest scenariu când mas, ina e alcatuita din aprox. 40 de tone de explozibil aprins. A pat,it-o Ariane5 la primul zbor. Procesorul a fost instruit sa scrie o valoare ce ocupa 8 octet,i într-o locat,ie unde erau rezervat,i doar 4.Au fost doar pagube materiale. Cautat,i "Ariane 5 first flight software bug". Asta s-a întâmplat în 1996. S, i daca asta vise pare preistorie, cautat,i "Boeing 747 Max crash". Problema nu a fost aceeas, i dar a t,inut tot de software. Din pacatecâteva sute de oameni au murit.

Tipul variabilei specifica doua lucruri vitale: (1) Cîta memorie trebuie alocata s, i (2) Cum interpretam, în operat,ii,pattern-ul de bit,i stocat acolo.

Tipul este un contract între noi, cei care folosim variabilele s, i cei care au creat microprocesoarele (respectiv auimplementat pe ele diverse operat,ii). Folosind variabilele noi spunem CE vrem sa se întâmple (ex adunarea) iar prinintermediul tipului procesorul s, tie CUM sa efectueze operat,ia (adunare în virgula mobila).

Conceptul de tip sta la baza programarii orientate pe obiect. Concepte ca încapsularea, polimorfismul, interfet,ele etcse pot explica foarte us, or interpretând conceptul de tip ca o legatura între nis, te bit,i s, i operat,iile care se pot face cuacei bit,i.

Haidet,i sa vedem câteva tipuri de date s, i cum este interpretat pattern-ul de bit,i de catre procesor. MARE ATENT, IE!!!Numarul de octet,i/bit,i ocupat de un tip de date este diferit de la o arhitectura la alta! Aici este doar un exemplu.

Table2.1: Câteva tipuri, dimensiunea memoriei rezervate s, i interval devalori posibile.

Tip Dimensiune bit, i Interpretare Interval de valorichar 8 Caracter (întreg fara semn) 0 la 255short 16 Întreg cu semn -32,768 la 32,767int 32 Întreg cu semn -2,147,483,648 la 2,147,483,647float 32 Virgula mobila 3.4 * 10−38 < |𝑖| < 3.4 * 1038double 64 Virgula mobila 1.7 * 10−308 < |𝑖| < 1.7 * 10308void* 8/16/32/64 Adresa de memorie Depinde de arhitectura sistemului

Deasemenea avem modificatori cum ar fi long sau unsigned. Modificatorul long rezerva mai mult,i bit,i. Mergepe int s, i double. unsigned, dupa cum îi zice s, i numele, modifica interpretarea pattern-ului de bit,i ca întreg farasemn.

Valorile întregi fara semn se pastreaza direct în codul binar 11 = 1 * 23+0 * 22+1 * 21+1 * 20 adica pentru numarul11 avem în memorie pattern-ul 1011. Restul bit,ilor sunt 0.

Numerele întregi cu semn se pastreaza ca si Two's complement iar cele în virgula mobila dupa standardulIEEE-754.

Mai încolo vet,i învat,a (s, i uita la fel de rapid) despre endianess. Pe scurt, aceasta not,iune specifica unde se alfa bitulsau octetul cel mai semnificativ într-un s, ir de bit,i sau octet,i. Deocamdata ret,inet,i ca în memorie bit,ii se scriu ca s, i pehartie. Numarul zecimal 34 este identic cu numarul 0000034. Nu conteaza zero-urile din fat, a. La fel, în binar, 11 esteidentic cu 0000 0000 0000 0011 (am lasat spat,ii pentru claritate)

Dimensiunea memoriei ocupata de un tip sau de o variabila se poate afla cu operatorul sizeof. Des, i se foloses, te cuparanteze, este considerat un operator s, i nu o funct,ie: sizeof(int) va spune cât,i octet,i sunt rezervat,i pentru tipulint pe platforma pe care ruleaza codul. În practica niciodata sa nu presupunet,i ca pentru un anumit tip sunt stocat,i unanumit numar de octet,i. Folosit,i sizeof în calcule. Exista însa s, i tipuri speciale pentru care compilatorul garanteazaun numar specific de octet,i. Nu intram acolo.4

4 Cautat,i specificat,iile stdint.h daca va trebuie dimensiune fixa de bit,i la anumite variabile, independent de arhitectura sistemului pentrucare se face compilarea.

14 Chapter 2. Iterat, ia 1

Page 21: Programarea Calculatoarelor - utcluj.rousers.utcluj.ro/~visoft/teach/programareacalculatoarelor.pdfProgramarea Calculatoarelor, Versiune 0.32 1.2Desfas˘, urare 1.2.1Abstractizare

Programarea Calculatoarelor, Versiune 0.32

Trebuie sa ret,inet,i ca atunci când lucram cu o variabila, compilatorul produce instruct,iuni care, în orice operat,ie încare este implicata variabila, înlocuies, te variabila cu cont, inutul memoriei definit de variabila. Aceste operat,ii sunttransparente pentru limbajul C. De aceea, când scriem de exemplu i + 2 compilatorul va crea instruct,iuni care aduccont,inutul memoriei lui i s, i apoi instruct,iuni care adauga 2 la acea valoare.

La fel se întâmpla s, i la atribuiri. i = -5 va fi tradus printr-o instruct,iune care scrie pattern-ul de bit,i corespunzatorlui -5 în locat,ia de memorie denumita i. Înainte de scriere, valoarea -5 va fi convertita într-un pattern de bit,i conformtipului variabilei i. Two's Complement daca i este declarat int sau IEEE-754 daca este double.

2.5.1 Vizibilitatea unei variabile

Scopul sau vizibilitatea unei variabile în cod. Instruct,iunea de declarare a unei variabile poate sa apara în exteriorulcodului (ex în afara funct,iei main) sau în interiorul unui bloc de instruct,iuni. Mai poate aparea în interiorul expresieide init,ializare de la for.

În funct,ie de locat,ia unde a fost declarata variabila aceasta este vizibila sau nu în alte zone ale codului. Variabila estevizibila în blocul curent dupa ce a fost declarata precum s, i în blocurile îmbricate în blocul curent. Variabila NU maieste vizibila (compilatorul îi spune sistemului de operare ca NU mai are nevoie de zona de memorie) dupa ce se iesedin blocul în care a fost declarata.

Daca într-un bloc îmbricat este declarata o noua variabila cu acelas, i nume, aceasta din urma o "ascunde" pe cea de penivelul superior.

Deocamdata cu aceaste reguli va vet,i descurca cu declaratul variabilelor. Le vet,i învat,a mai us, or, lucrând s, i observândefectele.

2.5.2 Alte observat, ii

Adresa de memorie a unei variabile difera de la o rulare la alta. Aceasta este o masura de securitate. Cineva (de obiceiun virus) care preia controlul calculatorului poate citi foarte usor datele din memorie daca s, tie la ce adresa se afla.Gândit,i-va ca va cites, te user/parola când va logat,i undeva. Din fericire, astfel de situat,ii sunt mai rare, tocmai datoritaalegerii aleatoare a locat,iei variabilelor, de catre sistemul de operare2. Evident, daca este important, inginerul poatespecifica locat,ia de memorie. Util daca lucrat,i pe dispozitive cu memorie foarte put,ina.

Ce este vital în a s, ti despre variabile:

• O variabila defines, te o locat,ie de memorie. În timpul execut,iei, numele este înlocuit în expresii de valoareastocata acolo.

• Cantitatea de memorie alocata este dictata de tipul variabilei.

• Exista operatori care pot lucra s, i direct cu adresa unei variabile. Dar în operat,iile unde astfel de operatori nusunt folosit,i, aducerea s, i scrierea valorilor din s, i în memorie este transparenta.

• Fiecare variabila are un tip s, i un nume. Tipul este necesar compilatorului atunci când efectueaza operat,ii curespectiva valoare. În procesor exista instruct,iuni separate pentru adunare cu semn, fara semn, în virgula mobila,etc. Compilatorul trebuie sa s, tie cu ce instruct,iune mas, ina sa efectueze operat,iile.

• Numele este util doar pentru noi. Acesta va fi convertit dupa compilare într-o adresa de memorie.

• NU putem lucra cu o variabila nedeclarata înainte. Asta înseamna ca instruct,iunea de declarare trebuie sa aparaînaintea primei utilizari, în blocul curent. Atent,ie la blocurile îmbricate s, i blocurile "surori"!

• Ignorarea regulilor de vizibilitate pot duce la bug-uri s, i erori de compilare.

2 Exista sisteme de "izolare" a programelor, la nivelul sistemului de operare. Un virus însa poate sa se "strecoare" s, i sa aiba aceleas, i drepturi cuOS-ul. Asta ignorând fenomene ca Spectre sau Meltdown (cautat,i pe internet "Meltdown and Spectre bugs" s, i va îngrozit,i)

2.5. Memoria si variabilele 15

Page 22: Programarea Calculatoarelor - utcluj.rousers.utcluj.ro/~visoft/teach/programareacalculatoarelor.pdfProgramarea Calculatoarelor, Versiune 0.32 1.2Desfas˘, urare 1.2.1Abstractizare

Programarea Calculatoarelor, Versiune 0.32

2.5.3 Init, ializarea variabilelor s, i constante

Ultimul punct atins despre variabile în aceasta iterat,ie este despre cum se init,ializeaza la declarare. Am vazut laexemplul cu for ca putem avea construct,ii de genul int i = 0 asta facând ca la locat,ia i sa pastram un întreg pe4 octet,i5 s, i sa init,ializam zona cu zero.

Cifra 0 este o constanta numerica în baza 10, de tip int. Adica constanta va ocupa 4 octet,i5 s, i este reprezentata cusemn! Putem modifica acest lucru de exemplu 0l pentru a o face de tip long int. Putem scrie constante în baza16, folosind prefixul 0x. Foarte des când lucram cu adrese. 0xA este 16.

Tipul standard pentru constante în virgula mobila este double. Putem folosi notat,ia s, tiint,ifica, 1e3 este 1000.

Mai avem câteva feluri de constante, caracter s, i s, ir de caractere. Un caracter este pus între apostrof ' iar s, irul decaractere între ghilimele ". De obicei un caracter ocupa un octet s, i este reprezentat fara semn. Exista o convent,ie înC, un s, ir de caractere este o zona de memorie terminata cu un octet ce cont,ine doar 0.

Foarte important, constanta 'a' ocupa un octet iar constanta "a" ocupa doi. Octetul cu pattern-ul de bit,i a caracteruluia urmat de octetul cu pattern-ul de bit,i al ciferi 0.

Este legal sa folosit,i o constanta caracter acolo unde se accepta un numar întreg. Este perfect valid sa scriet,i 'a' + 1.Expresia va lua valoarea 'b'. Sau valoarea zecimala 98. Depinde de cine interpreteaza pattern-ul de bit,i 01100010.

În apelul de funt,ie printf primul parametru este un s, ir de caractere. Compilatorul aloca automat memorie pentruacest s, ir de caractere iar la execut,ie se va copia adresa rezervata în interiorul codului de la printf. Vom reveni înIterat,ia 2 cu detalii.

2.6 Intrari s, i ies, iri

2.6.1 Ies, irea în consola text

Am tot vorbit de funct,ia printf acum a venit momentul sa vedem mai multe detalii. Nu uitat,i, într-o expresie,operatorul % defines, te operat,ia de modulo. S, i constanta de tip s, ir de caractere este o bucata de text (pe o linie)delimitata de ghilimele.

Examinând exemplele de mai sus (printf("%d ", i)) observam ca acest caracter % apare în interiorul s, iruluide caractere.

Funct,ia printf din biblioteca standard este responsabila de afis, area în consola a unui text. Funct,ia accepta ca primparametru un s, ir de caractere, urmat de zero sau mai mult,i parametri. Exista un cod complex în interiorul funct,iei caredescompune s, irul de la intrare, cauta caracterul % s, i pentru fiecare caracter % va cauta parametrul corespunzator dinrestul parametrilor de intrare. Complicat? Cam da.

Sa ne uitam la printf("%d ", i). Primul parametru al funct,iei este s, irul de caractere "%d " iar al doilea estevariabila i.

Codul din biblioteca standard, va începe sa interpreteze s, irul, caracter cu caracter. Daca nu e caracterul %, afis, eazacaracterul curent pe ecran. Când întâlnes, te %, intra într-un mod special.

1) Se identifica parametrul corespunzator din rîndul parametrilor de intrare. În cazul nostru, i.

2) Caracterul imediat de dupa % va defini modul în care, codul din printf va interpreta pattern-ul de bit,i aparametrului de intrare.

Construct,ia %d se numes, te specificator de format. Pentru fiecare tip, exista o litera diferita. Daca pe ecran vedet,ibalarii, o sursa ar fi specificatorul gres, it. Caz frecvent, afis, area unui float ca int. Într-un int, numarul este pastratca s, i Two's complement dar un float în standardul IEEE-754. Semnificat,ia bit,ilor este radical diferita!

5 Depinde de arhitectura, dar NU vom mai tot relua acest aspect.

16 Chapter 2. Iterat, ia 1

Page 23: Programarea Calculatoarelor - utcluj.rousers.utcluj.ro/~visoft/teach/programareacalculatoarelor.pdfProgramarea Calculatoarelor, Versiune 0.32 1.2Desfas˘, urare 1.2.1Abstractizare

Programarea Calculatoarelor, Versiune 0.32

Cât,iva specificatori utili: %d pentru int, %f pentru float, %c pentru caracter s, i %s pentru un s, ir de caractere.Se pot face formatari complexe, se poate controla numarul de zecimale, numarul minim de caractere afis, ate, formats, tiit,ific, afis, are în hexazecimal, etc etc. Va recomand sa studiat,i un pic documentat,ia funct,iei printf.

Important: Nu vi se va cere, la examen, sa s, tit,i pe de rost tot,i specificatorii de format! Dar cât,iva, ît,i vei folosi as, a dedes încât va vor intra în automatism. Necunoas, terea lor de obicei denota faptul ca at,i cam sarit activitat,ile de laborator.

Pâna acum nu ne-am legat de #include <stdio.h>. Pe scurt, aceasta este o comanda pentru prima componentadin toolchain (denumita preprocesor). Pe scurt, comanda va încorpora cont,inutul fis, ierului stdio.h în codul nostru.Apoi codul va fi livrat catre compilator. În stdio.h este declarata funct,ia printf. Numele stdio vine de laSTandard Input Output iar printf de la PRINT Formatted. Exista mai multe surori ale funct,iei:

• sprintf - String PRINT Formatted, care în loc sa "tipareasca" pe ecran, scrie output-ul într-o zona de memorie

• fprintf - File PRINT Formatted, care trimite output-ul într-un fis, ier.

Constantele de tip caracter se pastreaza pe 8 bit,i, adica putem avea maxim 255 de caractere. Cifra 0 (adica 00000000în binar) denota sfârs, itul s, irului s, i este tratata altfel. Corespondent,a dintre pattern-ul de bit,i s, i caracterul afis, at pe ecran(sau interpretat de procesor) este data de codul ASCII. S, i este o convent,ie universal acceptata în programare. Litereledin codul ascii sunt cele din limba engleza. Pentru ca în lume exista mult mult mai multe tipuri de litere (chinezes, ti,chirilice, diacriticele din limba Româna, Germana, Franceza. etc etc) codul ASCII a fost extins cu Unicode UTF-8(evident treburile sunt MULT mai stufoase). Acest cod foloses, te mai mult,i bit,i pentru a coda un caracter. Funct,iilecare proceseaza caractere (inclusiv printf) trebuie sa s, tie daca are de a face cu un s, ir de caractere ASCII sau s, iruleste codat în alt standard. Deocamdata insist sa NU FOLOSIT, I diacritice sau alte semne speciale în codul vostru! Niciîn codul sursa (comentarii, nume de variabila) s, i nici în constantele de tip caracter/text.

2.6.2 Citirea de la tastatura

La începutul materialului am povestit despre cum procesorul asculta daca s-a apasat sau nu o tasta s, i react,ioneaza laapasare. Exista un astfel de "circuit" (pâna la urma tot o bucata de cod) în calculator dar nu procesorul. Circuitul,înt,elege mesajele trimise de la tastatura s, i le stocheaza într-o zona speciala denumita buffer de tastatura. De aici,programul nostru le poate citi.

Funct,ia cea mai utila, care cites, te pattern-urile de bit,i din buffer-ul de tastatura s, i le decodifica conform specificat,iilornoastre este scanf.

scanf vine de la SCAN Formatted. Se gases, te tot în biblioteca stdio.h Ca s, i la printf avem variantele sscanf s, ifscanf pentru a decoda s, iruri de caractere s, i respectiv, a citi din fis, iere.

Funct,ia scanf accepta un parametru s, ir de caractere care cont,ine nis, te specificatori de format. Urmatorii parametrisunt adresele de memorie unde va fi stocat rezultatul decodificarii.

Deocamdata va arat apelul de funct,ie s, i intram în detalii mai încolo. Pentru a citi un întreg trebuie sa:

• Avem o zona de memorie în care sa putem stoca întregul (declaram o variabila)

• Utilizam %d ca specificator de format

• Trimitem adresa de memorie unde dorim sa se faca memorarea, la scanf.

Atent,ie, dupa cum spuneam la capitolul despre funct,ii, compilatorul copiaza cont,inutul memoriei parametrilor careîi dam la funct,ie, în zona de memorie a variabilelor definite în funct,ie. Aici e un pic de subtilitate, pentru ca, dacaapelam o funct,ie f(i), funct,ia va primi o copie a zonei de memorie i în momentul apelulu. Daca funct,ia va vroi samodifice ceea ce se afla la adresa lui i va trebui sa lucreze direct cu adresa de memorie a variabilei i.

Aceasta capacitate a limbajului C de a lucra direct cu adresele de memorie (În curând faimos, ii s, i mult urât,ii operatori* s, i &) dau o fort, a inimaginabila limbajului C. S, i ca orice putere mare, ea se poate folosi ca sa facem lucruri frumoasesau ca programatorul sa îs, i dea peste degete. Depinde de voi! Bun, revenind, deocamdata e suficient sa s, tit,i ca lucrul

2.6. Intrari s, i ies, iri 17

Page 24: Programarea Calculatoarelor - utcluj.rousers.utcluj.ro/~visoft/teach/programareacalculatoarelor.pdfProgramarea Calculatoarelor, Versiune 0.32 1.2Desfas˘, urare 1.2.1Abstractizare

Programarea Calculatoarelor, Versiune 0.32

cu memoria este mai dificil de stapânit DAR este un element central în C. (vital în meseria de Inginer s, i bineînt,eles,temeinic verificat la examen)

Exemplu de citire a unui întreg. Vet,i putea folosi instruct,iunile de mai jos:

#include <stdio.h>int main(){

int i;scanf("%d", &i);printf("Am citit %d", i);return 0;

}

Se declara variabila i iar singurul lucru diferit este modul în care se trimite i ca parametru la scanf. Scriem &ipentru a afla s, i trimite adresa rezervata pentru i. Valoarea adresei lui i va fi copiata într-o zona de memorie rezervatade codul scanf, zona de memorie capabila sa manipuleze adrese de memorie.

Intern scanf se as, teapta ca pentru fiecare caracter % din s, irul de caractere primit sa primeasca s, i o adresa. Atent,ie, vaprimi o adresa pe care o va putea manipula. Aceasta adresa va fi stocata într-o variabila speciala, variabila care poatemanipula adrese. În Tabelul 2.1 avem tipul void* care permite exact acest lucru.

Codul din scanf cites, te buffer-ul de tastatura s, i convertes, te s, irul de caractere gasit acolo conform specificatoruluide format. Dupa conversie pattern-ul de bit,i va fi stocat la adresa de memorie primita ca parametru. Speficicatorul deformat spune s, i câta memorie va fi "umpluta" cu acest pattern de bit,i. Nu exista posibilitatea ca scanf sa s, tie câtamemorie este rezervata.

Un exemplu pe codul anterior: Daca la specificatorul de format avem %d s, i în buffer sunt caracterele -1386 laadresa lui i se va stoca pattern-ul de bit,i 11111111111111111111111101110110 adica valoarea -138 în Two'scomplement presupunând o arhitectura unde int ocupa 4 octet,i (32 bit,i).

Gres, eli frecvente care se fac:

1) Uitat,i sa trimitet,i o adresa (mai ales când vet,i începe sa lucrat,i cu variabile ce memoreaza adrese)

2) Specificatorul de format nu corespunde cu tipul variabilei. scanf va coda ceea ce a citit într-un pattern de bit,icorespunzator. Daca variabila declarata are un tip incompatibil (ex float vs int) vor aparea probleme

3) Nu avet,i memorie rezervata la adresa trimisa spre scanf. Eroare de obicei corelata cu (1).

Ca ultim exercit,iu, modificat,i codul de afis, are a numerelor pare pentru a afis, a primele numere pare non negative maimici decât un întreg citit de la tastatura.

Dupa ce va iese, adaugat,i cod de validare a intrarii. Numarul citit trebuie sa fie non negativ, sa fie rezonabil de micpentru a încapea pe ecran, etc. Afis, at,i mesaje pentru fiecare situat,ie invalida. Nu uitat,i de abordarea iterativa! Lafiecare modificare, compilat,i s, i rulat,i! Nu scriet,i mult cod fara sa îl verificat,i!

Succes!6 Adica caracterele -, 1, 3, 8, urmat de un caracter alb.

18 Chapter 2. Iterat, ia 1

Page 25: Programarea Calculatoarelor - utcluj.rousers.utcluj.ro/~visoft/teach/programareacalculatoarelor.pdfProgramarea Calculatoarelor, Versiune 0.32 1.2Desfas˘, urare 1.2.1Abstractizare

Programarea Calculatoarelor, Versiune 0.32

2.7 Lean

Am vazut un prim element fundamental al abordarii de tip lean. Iterat,ia. Am vazut-o în exemplele de laborator, amvazut-o la modul în care vi s-au prezentat cunos, tint,ele de C.

Ce nu at,i vazut pâna acum este modul în care alegem "subproblemele" la care lucram. Exista abordarea standard,folosita în mediul de startup (business) sau în cercetare. A doua abordare este cea pe care o facet,i când învat,at,i ceva.

2.7.1 Abordarea standard

Daca vrei sa ai succes cu business-ul, metodologia spune ca trebuie sa es, uezi. Sa es, uezi rapid, sa es, uezi ieftin. Lucrulsau scopul cu care pornes, ti iterat,ia este de obicei cel mai riscant lucru pentru business. De ce? Pentru ca daca NUîl pot,i rezolva sau ocoli, nu are rost sa te apuci de respectiva treaba. Aici intra poate mai put,in dificultat,i tehnice(exceptând cazul în care vrei sa rezolvi teleportarea) s, i mai mult dificultat,i ce t,in de piat, a.

Ca business, va trebui sa raspundet,i rapid la întrebari de genul "Va exista cerere?" "Voi putea vinde cu pret,ul X?" etcetc.

La fel este s, i în proiectele experimentale, orientate pe cercetare. Se formuleaza ipoteze care sunt validate sau infirmateîn urma implementarii s, i efectuarii experimentului. Odata ce o ipoteza este verificata, se pate trece la urmatoarea.

2.7.2 Abordarea când învat,at, i

Daca la abordarea standard, t,elul mare era de a realiza ceva (venituri financiare mari, succes în cariera de cercetator),aici scopul fundamental este de a acumula sau sedimenta cunos, tint,e noi.

Va recomand ca la abordarea iterativa a problemelor sa începet,i tot timpul cu ceea ce s, tit,i. Fiecare noua iterat,ie sa vaîndrepte spre zona necunoscuta, pas cu pas. As, a, atunci când "ceva nu iese" suntet,i siguri ca nu iese din cauza iterat,ieicurente s, i nu din cauza ca at,i gres, it ceva în urma cu 100 de linii de cod. Asta deoarece acele linii au fost scrise într-oiterat,ie anterioara. Iterat,ie, care, ca finalitate, a avut s, i o parte de testare a ceea ce at,i implementat.

Mai jos sunt aratat,i principalii pas, i ai metodologiei Lean.

Selectare problem 

Testare Proiectare

Implementare

Figure2.1: Pas, ii metodologiei lean. Atent,ie, ciclul se reia pâna când problema este gata.

Selectare problema Va aleget,i o subproblema cunoscuta, de exemplu citirea de la tastatura. Subproblema se mainumes, te s, i obiectivul iterat,iei. Daca pe parcurs acest obiectiv se schimba, iterat,ia se "reseteaza"

Proiectare Va gândit,i cum sa abordat,i problema propusa. Va putet,i folosi de o hârtie s, i creion. Putet,i cauta pe internetdocumentat,ii, how-to sau putet,i decide ca problema e prea complexa s, i mai trebuie împart,ita în mai multe bucat,i. Înacest caz, revenit,i la pasul anterior. (Aleget,i o noua subproblema)

Implementarea Scriet,i cod conform pasului de la proiectare. Compilat,i, rulat,i codul, asigurat,i-va ca ceea ceimplementat,i este valid. Dat,i un input la problema s, i asigurat,i-va ca nu at,i scris ceva gres, it. Doar cine nu munces, te,nu gres, es, te.

2.7. Lean 19

Page 26: Programarea Calculatoarelor - utcluj.rousers.utcluj.ro/~visoft/teach/programareacalculatoarelor.pdfProgramarea Calculatoarelor, Versiune 0.32 1.2Desfas˘, urare 1.2.1Abstractizare

Programarea Calculatoarelor, Versiune 0.32

Testarea S, tit,i care este obiectivul iterat,iei, acum este momentul sa va asigurat,i ca ceea ce at,i implementat corespunde.Putet,i rezolva pe hârtie câteva cazuri particulare pe care le introducet,i la input s, i observat,i ies, irea. De cele mai multe oriaceasta etapa presupune s, i scriere de cod (ex. printf-uri). (Poate la alte materii vet,i auzi s, i de testarea regresiva sauUnit Testing unde codul de test, este considerat parte integranta din proiect s, i este verificat la fiecare modificare aproiectului.) Daca identificat,i bug-uri, corectat,i. Daca at,i identificat o eroare de logica, (ex. at,i ratat obiectivul) reluat,itot procesul. Probabil ca etapa de proiectare a fost necorespunzatoare.

Iterat, i Cel mai important lucru la aceasta abordare! Repetat,i procesul de mai sus! Important, NU s, terget,i codul detest. Comentat,i-l. Sigur va va trebui în alte iterat,ii când vet,i face modificari la codul deja scris. Cu fiecare iterat,ietrebuie sa va apropiat,i de lucrurile necunoscute sau mai grele ale problemei mari. Când vet,i ajunge la ele, va vet,iputea focusa strict la ele, deoarece s, tit,i ca elementele de care depinde aceasta problema mai grea, exista s, i sunt corectimplementate.

2.7.3 Abordarea când stapânit, i instrumentele

La un moment dat vet,i ajunge sa s, tit,i destul de bine limbajul de programare. Nu este foarte productiv sa începet,i culucrurile cunoscute. Putet,i sa abordat,i "din prima" subproblema mai grea. Poate avet,i nis, te cerint,e mai deosebite (exsortare). Va putet,i apuca sa rezolvat,i sortarea, dupa care sa abordat,i citirea s, i/sau afis, area datelor.

Pas cu pas, pe masura ce acumulat,i tot mai multe cunos, tint,e s, i instrumente, vet,i ajunge sa abordat,i în prima iterat,ielucrul cel mai riscant/necunoscut. Despre restul, vet,i putea estima atât volumul de munca necesar cât s, i instrumentelecare le vet,i folosi.

Succes!

20 Chapter 2. Iterat, ia 1

Page 27: Programarea Calculatoarelor - utcluj.rousers.utcluj.ro/~visoft/teach/programareacalculatoarelor.pdfProgramarea Calculatoarelor, Versiune 0.32 1.2Desfas˘, urare 1.2.1Abstractizare

CHAPTER 3

Iterat, ia 2

3.1 Introducere

În acest moment avet,i câteva "blocuri" cu care puteti construi programe simple. Iterat,ia curenta va aprofunda not,iunileînvat,ate în iterat,ia 1 fara a adauga multe elemente complet noi. Vom merge pe verticala. Atentie, NU veti deveniexperti în C sau în Programarea Calculatoarelor nici dupa aceasta iterat,ie s, i nici la finalul cursului. Va mai trebuiemulte cunos, tint,e de la alte materii precum s, i cât,iva ani buni de lucru în product,ie. Cursul acesta va învat, a not, iunilede baza care va vor fi utile în a controla un procesor s, i a putea sa va adaptat, i rapid la noi limbaje de programaresau arhitecturi. Cam tot ce exista acum pe piat, a, ca hardware, (profesionist sau amator) se poate controla cu ajutorulC-ului.

Am lasat discut,ia în punctul în care s, tit,i sa va rezervat,i memorie, sa efectuat,i calcule matematice simple, sa folosit,ivariabilele în instruct,iuni de control, s, i sa citit,i/scriet,i cont,inutul variabilelor folosindu-va de tastatura s, i ecran.

În iterat,ia curenta intram mai în detaliu despre cum lucreaza procesorul cu memoria s, i cum organizeaza limbajul Cmemoria.

3.2 Tipuri de memorie accesibile programului

La cel mai jos nivel memoria este vazuta ca un s, ir de locat,ii, fiecare locat,ie putând stoca 8 bit,i. La nivel de aplicat,ieînsa exista mai multe categorii de memorie, fiecare cu specificul ei. Aceasta categorisire este impusa de arhitecturaprocesorului s, i de sistemul de operare. S, i este o convet,ie între programatori s, i cei care au realizat arhitectura.

Avem memoria care t,ine codul, care memoreaza programul nostru. Nu scriem în aceasta memorie dar putem facereferire prin adrese, la adrese (bucat,i de cod sau date) de aici.

Avem apoi memoria de lucru. Numita Heap. Aceasta este gestionata de funct,ii speciale din C. Cu ajutorul sistemuluide operare, capacitatea acestei memorii poate depas, i dimensiunea memoriei fizice (RAM) disponibila pe calculator.Orice date care ocupa spat,iu mult vor trebui stocate aici.

Exista memoria statica, care este alocata de compilator. Aici se pastreaza variabilele care sunt vizibile în tot codulprecum s, i constantele pentru care trebuie memorie.

21

Page 28: Programarea Calculatoarelor - utcluj.rousers.utcluj.ro/~visoft/teach/programareacalculatoarelor.pdfProgramarea Calculatoarelor, Versiune 0.32 1.2Desfas˘, urare 1.2.1Abstractizare

Programarea Calculatoarelor, Versiune 0.32

Pentru a gestiona apelurile de funct,ii s, i pentru a stoca un anumit fel de variabile exista o zona de memorie numitastiva. Este o zona mica ca dimensiune, care se umple repede daca nu suntet,i atent,i. Erorile de stiva sunt foarte foartefrecvente, de unde s, i numele celui mai celebru site cu raspunsuri pentru programatori: Stack Overflow1

Locul unde se aloca variabilele s, i cantitatea de memorie disponibila depinde de locul s, i modul cum lucram cuvariabilele.

3.2.1 Variabile globale fis, ierului curent

Acestea sunt declarate în exteriorul oricarei funct,ii. Ele sunt vizibile din locul în care au fost declarate pâna la sfârs, itulfis, ierului curent.

Exista posibilitatea ca în programul nostru sa avem mai multe fis, iere. Vom vedea mai încolo cum "comunicam" s, i cumpartajam memorie între diverse bucat,i din program (Un fis, ier .c este considerat un modul al programului)

Aceste variabile pot fi schimbate de orice funct,ie din fis, ierul curent. De aceea este de preferat a se evita folosirea lor.Exista modificatori (cuvântul cheie const) care împiedica acest lucru. Îl vom atinge mai încolo.

Variabilele acestea se aloca în zona statica a programului. Alocarea s, i init,ializarea lor se face înainte de execut,iaprimei linii din funct,ia main(). Este treaba compilatorului sa realizeze acest lucru.

Deasemenea, în funct,ie de compilator, pot aparea probleme daca exista alte variabile declarate în alte module, cuacelas, i nume.

Recomandarea ar fi sa le folosit,i doar pentru constante sau setari globale ale aplicat,iei.

3.2.2 Variabile locale funct, iei/blocului curent

Pe scurt, acestea sunt variabilele declarate între acolade. Ele sunt alocate pe stiva. Dar sa intram în detalii. La intrareaîntr-o funt,ie sau bloc de instruct,iuni compilatorul aloca pe stiva toate declarat,iile de variabile întâlnite în bloc/funct,ie.La ies, ire din bloc/funct,ie, ele sunt s, terse (dealocate).

Pe stiva se pastreaza s, i parametrii de intrare ai funct,iei (vom vedea mai multe la funct,ii).

Acelas, i rat,ionament se aplica s, i la blocuri de instruct,iuni. De exemplu, o variabila declarata în interiorul unui ciclufor, va fi alocata pe stiva s, i va fi s, tearsa când execut,ia codului va ies, i din blocul for.

Acesta este motivul pentru care variabilele din interiorul unui bloc sau a unei funct,ii NU sunt vizibile in exterior, darsunt vizibile în blocurile aflate în interiorul blocului curent.

Atent,ie, apelul unei funct,ii presupune intrarea într-un nou bloc de instruct,iuni. Indiferent de unde este apelata funct,ia,codul din funct,ie NU "vede" memoria alocata în blocul de unde a fost apelata.

Sa vedem câteva exemple:

Listing 3.1: Vizibilitatea variabilelor

1 int g1;2 void some_func(){3 int a, b;4 {5 int i, b;6

7 }8

9 }10

(continues on next page)

1 https://stackoverflow.com/

22 Chapter 3. Iterat, ia 2

Page 29: Programarea Calculatoarelor - utcluj.rousers.utcluj.ro/~visoft/teach/programareacalculatoarelor.pdfProgramarea Calculatoarelor, Versiune 0.32 1.2Desfas˘, urare 1.2.1Abstractizare

Programarea Calculatoarelor, Versiune 0.32

(continuare din pagina precedenta)

11 int main()12 {13 int i, j;14 some_func();15 return 0;16 }

• Variabila g1 este vizibila (poate fi folosita) peste tot.

• Variabilele i s, i j declarate la linia 13 sunt vizibile doar în codul funct,iei main() Ele NU sunt vizibile înfunct,ia some_func() sau în alta funct,ie din program

• Variabilele i s, i b declarate la linia 5 sunt vizibile doar pâna la linia 7

• Variabila i din linia 5 este diferita (ca locat,ie s, i vizibilitate) de variabila i din linia 13. Ele se pot folosiindependent s, i nu au efect una asupra celeilalte.

• Variabila b din linia 5 "ascunde" variabila b din linia 3. Adica, orice operat,ie între liniile 5 s, i 7 vor afecta zonade memorie declarata la linia 5 s, i va ignora zona de memorie de la linia 3. Odata ies, it din blocul acesta (adicadupa linia 7) variabila b declarata în linia 3 va fi din nou vizibila.

Aceste reguli va ajuta sa refolosit,i numele variabilelor în diverse funct,ii sau blocuri de cod. Atent,ie mare doar laascunderea variabilelor.

Variabilele definite local se numesc s, i variabile automatice pentru ca, de dealocarea lor se ocupa automat compilatorul.Aceasta este o facilitate foarte importanta, prezenta s, i în alte limbaje de programare.

Când at,i modificat codul de la instruct,iunea for (Cod 2.2) adaugând doar o instruct,iune printf dupa terminareaciclului probabil ca at,i obt,inut o eroare de compilare. Tocmai din cauza ca variabila i a fost dealocata de compilatorcând s-a ies, it din blucla for.2

Important: Stiva are dimensiune limitata! Aproximativ 64 KB, din care o parte este deja consumata de sistemul deoperare, înainte de a se începe execut,ia programului.

Sunt doua moduri uzuale în care se poate depas, i stiva:

• Alocarea unei bucat,i de memorie mare (eg un tabel cu câteva zeci de mii de valori)

• Alocarea de foarte multe ori a unei bucat,i relativ mici de memorie, prin apel recursiv.

În general, pentru variabile simple, contoare, variabile temporare, se poate folosi declararea locala.

Pentru informat,iile citite de la diverse intrari, pentru datele cu volum mare (ex s, iruri de caractere, matrici, etc) sefoloses, te memoria heap.

Mare atent,ie, la rezervarea memoriei, de obicei NU are loc s, i init,ializarea cu zero a zonei de memorie. Se întâmplacând compilatorul este setat în modul Debug sa adauge s, i o init,ializare cu zero. Dar acest lucru nu este garantatatunci când codul este compilat pentru a fi pus pe dispozitiv sau livrat la client (modul Release). As, a ca daca primafolosire a variabilei este ceva de genul i = i + 1 s-ar putea sa avet,i parte de bug-uri foarte dificil de localizat.

Acest tip de bug-uri se încadreaza perfect în expresia "But it works on my computer!"

2 Unele compilatoare nu dealoca variabilele declarate în interiorul primei expresii din instruct,iunea for. Mai ales cele de la Microsoft. De aceea,câteodata, astfel de construct,ii "merg".

3.2. Tipuri de memorie accesibile programului 23

Page 30: Programarea Calculatoarelor - utcluj.rousers.utcluj.ro/~visoft/teach/programareacalculatoarelor.pdfProgramarea Calculatoarelor, Versiune 0.32 1.2Desfas˘, urare 1.2.1Abstractizare

Programarea Calculatoarelor, Versiune 0.32

3.2.3 Accesarea memoriei Heap

Foarte pe scurt (vom face mai întâi pointerii), exista doua funct,ii de manipulare a acestei zone de memorie.

• Funct,ia malloc care rezerva memorie

• Funct,ia free care notifica sistemul de operare ca nu mai e nevoie de bucata rezervata.

Aceste funct,ii opereaza cu variabile de tip adresa de memorie. Adica, pattern-ul de bit,i ce va fi stocat la adresarezervata variabilei va fi interpretata de compilator ca o adresa. Complicat? Pare îmbârligat? Mai citit,i de 3 ori frazaanterioara pentru ca, în mod uzual vet,i lucra cu variabile care stocheaza, la adresa de memorie rezervata lor, valoareaunei adrese, adresa unde se stocheaza valoarea altei adrese spre o zona de memorie unde se afla un întreg sau float.Da, s, tiu, bine at,i venit la Programarea Calculatoarelor!

3.2.4 Declararea variabilelor

Câteva remarci referitoare la cum declaram variabilele. Pâna acum at,i vazut, instruct,iunea este destul de simpla:

tip nume;

Pot avea mai multe nume separate prin virgula, toate având acelas, i tip. Exemplu: int i, j, k;.

În cazul instruct,iunii de mai sus, TOT CE SE ALFA ÎN STÂNGA NUMELUI ESTE TIPUL VARIABILEI.Aceasta observat,ie pare banala dar cont,ine nis, te subtilitat,i:

De exemplu, unsigned int a;. Tipul lui a este unsigned int. Nimic greu, pâna când dat,i de as, a ceva:struct clasa *c;. Perfect valid. Tipul lui c este un pointer la o structura de date numita clasa.

Listing 3.2: Exemplu de tip compus, unde regula de mai sus se aplica

struct {int a;int b;

} s1;

Tipul lui s1 este tot ceea ce se alfa în stânga. Ca s, i la orice regula, avem s, i except,ii (le vedem mai încolo)

La declarare, de obicei pot sa fac s, i init,ializarea cu o constanta. De exemplu:

int a = 10;int b = 0xAF30;char c = 'A';float f = 34.2f;

3.3 Variabile (date) compuse

Am vazut cum putem lucra cu variabile simple, unde fiecare nume defines, te o locat,ie de memorie. De cele mai multeori acest lucru nu este suficient. Vrem de exemplu sa stocam un s, ir de valori citit de la o sursa externa (de exemplu oimagine sau un sunet) Nu putem stoca fiecare parte din imagine în câte o variabila.

Mai sunt situat,ii în care vrem sa grupam mai multe tipuri de date, pentru a descrie un concept. Ves, nicul exempluclasic, conceptul de persoana. Trebuie sa stochez nume, data nas, terii, CNP-ul, poate adresa, ceva ID-uri speciale, etc.Pot declara variabile pentru fiecare element dar ce fac daca am nevoie sa descriu 10 persoane?

24 Chapter 3. Iterat, ia 2

Page 31: Programarea Calculatoarelor - utcluj.rousers.utcluj.ro/~visoft/teach/programareacalculatoarelor.pdfProgramarea Calculatoarelor, Versiune 0.32 1.2Desfas˘, urare 1.2.1Abstractizare

Programarea Calculatoarelor, Versiune 0.32

3.3.1 Tablouri

Pe scurt, un tablou este o bucata de memorie continua, care ajuta în situat,iile în care avem nevoie sa stocam acelas, i tipde date. Iaras, i exemplul clasic, un vector unidimensional de întregi.

Listing 3.3: Declararea unui tablou simplu

int tab[4] = {0, 1, 2, 3};

Declarat,ia mai completa a unui tabel arata astfel:

Listing 3.4: Declararea unui tablou (sintaxa completa)

tip nume[dim1][dim2] ... [dimn] = {{ ... {v1, v2, v3 ... } ... }};

Am prezentat mai sus s, i declararea s, i init,ializarea. Spre deosebire de o variabila simpla avem parantezele drepte s, iacoladele dupa egal.

dim1 ... dimn specifica câte elemente avem pe fiecare axa. Aceste expresii trebuie sa poata fi evaluate la compilare3.Init,ializarea se face specificând în acolade, elementele. Trebuie sa avem EXACT atâtea nivele de îmbricare s, i EXACTatâtea elemente câte am declarat.

Partea de init,ializare este opt,ionala.

În instruct,iunea de mai sus am "definit" un hipercub cu n dimensiuni (sau un tensor). În practica rar trecem de 3dimensiuni.

Sa dam câteva exemple:

• int tab[10]; Tabel de întregi cu 10 elemente.

• char sir[100] = "Popescu"; va aloca 100 de caractere iar în primele 7 va scrie Popescu. Al 8-leava fi 0. Vom vedea la s, irurile de caractere, mai multe detalii.

• double coef[3] = {3.32, 4.21, 334}; Tabel cu 3 double la care li se seteaza valorile init,iale.

• double arr[2][100]; Un tabel cu doua rânduri s, i 100 de elemente pe fiecare rând (100 de coloane)

Daca declarat,ia tabloului are loc global atunci init,ializarea are loc la compilare. Ca s, i la dimensiuni, valorile cu carese face init,ializarea trebuie cunoscute la compilare.

Daca declarat,ia este locala, init,ializarea se face la execut,ie as, a ca putem avea orice expresie legala ca s, i elemente deinit,ializare.

Mai exista o forma de init,ializare în care nu specificam limitele dimensiunilor, acestea putându-se dedude dininit,ializare:

int tab[] = {1, 2, 5, 2};char sir[] = "Popescu";

Compilatorul va s, ti ca acest tabel (tab) are dimensiunea de 4 elemente iar sir 8. Din pacate nu funct,ioneaza decâtcu prima dimensiune. Restul, trebuie specificate manual.

În cazul tabelelor multidimensionale, în memorie, tabelele se stocheaza în modul C contiguous. Asta înseamnaa cavalorile consecutive de pe un rând sunt consecutive în memorie. S, i ca dupa ultimul element de pe rândul k urmeazaprimul element de pe rândul k+1.

3 Unele compilatoare, accepta, la declararea unui tabel local, ca expresia sa cont,ina calcule ce nu se pot face decât în momentul execut,iei codului(ex. depind de parametrii funct,iei). Cautat,i în specificat,iile C99 si C++11 acest lucru ca sa va convinget,i ca trebuie sa evitat,i astfel de construct,ii

3.3. Variabile (date) compuse 25

Page 32: Programarea Calculatoarelor - utcluj.rousers.utcluj.ro/~visoft/teach/programareacalculatoarelor.pdfProgramarea Calculatoarelor, Versiune 0.32 1.2Desfas˘, urare 1.2.1Abstractizare

Programarea Calculatoarelor, Versiune 0.32

Important: La declararea de tabele locale trebuie avut grija la dimensiune. Ca orice variabila locala, memoria pentrutabel este alocata pe stiva. Tabele mici, de câteva sute de elemente se pot aloca pe stiva. Dar pentru valori mai mari,vet,i vedea faimosul mesaj de eroare.

Bun, am vazut cum se declara un tablou, cum se init,ializeaza, haidet,i sa vedem cum se foloses, te în expresii!

Important: C-ul este un limbaj "Zero based". Asta înseamna ca orice index începe de la valoarea zero, ca primulelement din orice îns, iruire are indexul zero. Majoritatea limbajelor sunt zero based. Except,ie notabila, MATLAB

Accesul la un element se face simplu: nume_tablou[index]. Construct,ia va fi înlocuita cu valoarea stocata laindex. Elementele unui tablou se pot folosi peste tot unde este valid sa se foloseasca un operand de tipul respectiv. Sepot utiliza s, i se pot stoca valori în tabel:

tab[1] = 10; // Stochez la indexul 1, valoarea 10. Adica a 2-a valoare din tabel va→˓primi pattern-ul de bit,i 00000...1010k = tab[i] * 3;// Iau al 4-lea element din tabel, îl înmult,esc cu 3 s,i stochez totul→˓în variabila k.

Simplu? Da! La ce trebuie sa fit,i atent,i este ca primul element din tabel are indexul ZERO. La celalalt "capat" indexulmaxim este dim1 - 1

Adica:

• Am o declarat,ie int tab[4];

– tab[0] este valid, lucrez cu primul element din tabel

– tab[3] este valid, lucrez cu ultimul element din tabel

– tab[4]Am deja o problema. Expresia ma duce la al 5-lea element, dar nu am rezervate decât 4 elemente.Probabil va da o eroare de logica sau de rulare.

• În contextul declarat,iilor char sir[] = "Popescu" s, i double arr[2][100]

– sir[0] este 'P' sau numarul 80

– sir[4] este 's' sau numarul 115

– sir[7] este 0. Atent,ie, pattern-ul de bit,i 0, nu caracterul ASCII 0.

– arr[1][14] este al 15-lea element de pe ultimul rând din matricea arr

Parantezele drepte se numesc operatorul de indexare. Din fericire ele apar în expresii doar la indexare, adica la calcululcu adrese.

Vom vedea la pointeri cum funct,ioneaza intern tabelele, deocamdata ret,inet,i ca la compilare sau la execut,ie NU seface nici o verificare asupra indexului. Expresia tab[-3] este perfect compilabila s, i este executata de catre procesor.Probabil ca sistemul de operare va termina execut,ia, daca, locat,ia de memorie nu este rezervata programului.

Daca avem nevoie sa vedem câta memorie ocupa un tabel, putem folosi operatorul sizeof(nume_tabel).Valoarea este sizeof(tip) * dim1 * dim2 * ... adica dimensiunea ocupata de un element înmult,it cunumarul total de elemente.

Listing 3.5: Un exemplu simplu de lucru cu tabele. Citirea unui s, ir denumere de la tastatura s, i afis, area lui pe ecran

1 #include <stdio.h>2 #include <stdlib.h>

(continues on next page)

26 Chapter 3. Iterat, ia 2

Page 33: Programarea Calculatoarelor - utcluj.rousers.utcluj.ro/~visoft/teach/programareacalculatoarelor.pdfProgramarea Calculatoarelor, Versiune 0.32 1.2Desfas˘, urare 1.2.1Abstractizare

Programarea Calculatoarelor, Versiune 0.32

(continuare din pagina precedenta)

3

4 #define N 25

6 int main(void) {7 printf("Introduceti numerele: ");8 int tab[N];9 int i;

10 for(i = 0; i < N; i++){11 scanf("%d", &tab[i]);12 }13 printf("\nSe afiseaza tabelul:");14 for(i = 0; i < N; i++){15 printf("%d ", tab[i]);16 }17 return 0;18 }

3.3.2 Structuri

Al doilea tip de data compusa este acela în care avem nevoie de a grupa date eterogene împreuna. Tipul de date carene permite acest lucru se numes, te structura. Din nou, un tip de data gasit frecvent în alte limbaje de programare

Listing 3.6: Declararea unei structuri. Crearea unui nou tip.

1 //Structura e declarata la nivel global2 .....3 //Avem cateva cuvinte cheie4 typedef struct{5 //declaratii de variabile care vor face parte din structura. (Membrii structurii)6 int zi, luna, an;7 float milisecunde;8 } Data_Calendar; // Data_Calendar este numele noului tip creat9

10 // ............ undeva într-o funct,ie11

12 // Declararea unei variabile13

14 Data_Calendar d1;15 Data_Calendar d2 = {28, 06, 1971, 57600000};16

17 // Accesarea elementelor din structura18 d1.zi = 23;19 float valoare_secunde = d1.milisecunde / 1000;

În exemplul anterior avet,i exemplificate toate operat,iile cu struct. Declarare de structura, declarare de variabile pebaza structurii, accesare elemente.

Felicitari, acum s, tit,i sa creat,i un tip nou s, i sa îl folosit,i în a declara variabile de acest tip nou creat. În codul de maisus at,i specificat numele noului tip, câta memorie ocupa s, i câteva noi operat,ii ce se pot executa cu variabilele de acesttip, s, i anume, accesul la elemente. Avem expresii cu operatorul binar . urmat de numele membrilor.

De obicei, declararea tipului se face în exteriorul funct,iilor, astfel încât definit,ia sa fie vizibila peste tot în fis, ier.

Construct,ia typedef ..... nume se numes, te definirea unui tip, Suna pompos dar practic este doar un syntacticsugar. Compilatorul va înlocui peste tot unde gasets, te nume definit,ia de tip scrisa între nume s, i cuvântul cheietypedef.

3.3. Variabile (date) compuse 27

Page 34: Programarea Calculatoarelor - utcluj.rousers.utcluj.ro/~visoft/teach/programareacalculatoarelor.pdfProgramarea Calculatoarelor, Versiune 0.32 1.2Desfas˘, urare 1.2.1Abstractizare

Programarea Calculatoarelor, Versiune 0.32

Din momentul declararii tipului Data_Calendar putem sa îl folosim peste tot unde este legal sa apara un tip.Inclsiv în declarat,ia unui tabel. Folosim des tabele de structuri (ex lista de student,i) sau structuri care au cam membritabele (eg numele studentului) sau chiar alte structuri.

Daca avem stucturi îmbricate putem accesa fiecare element. Construct,ia studenti[4].materie[3].nota_colocviu este plauzibila. Sa speram ca este >=5 indiferent de indexul studentului sau materiei.

Stocarea în memorie a structurilor este mai complicata dar în principiu va putet,i gândi ca elementele sunt consecutiveîn memorie, în ordinea declararii lor. Atent,ie, memoria pentru membrii structurii se face în momentul declararii uneivariabile de tipul structura s, i NU în momentul declararii tipului structurii. Adica, în linia 12 s, i 13 S, I NU în liniile2-6. Daca vret,i, putet,i gândi codul din liniile 2-6 ca un s, ablon dupa care, compilatorul va aloca memorie atunci cândse vor declara variabile dupa acel s, ablon.

Regulile de rezervare pe stiva se respecta. As, a ca trebuie sa avet,i grija când alocat,i pe stiva structuri mari.

Operatorul sizeof() funct,ioneaza cum e de as, teptat. Din cauza ca organizarea în memorie a variabilelor dintr-o structura este mai complicata, valoarea la care se evalueaza sizeof este de obicei mai mare sau egala cu sumamemoriei variabilelor membru.

3.3.3 S, iruri de caractere

În limbajul C s, i în multe limbaje bazate pe C, s, irul de caractere, prin convent,ie, este o zona de memorie continuaterminata de valoarea zero. Fiecare element din s, ir este un octet. Funct,iile care proceseaza s, iruri de caractere seas, teapta ca pattern-ul de bit,i din fiecare octet sa corespunda codului ASCII al unui caracter.

O constanta tip s, ir de caractere este evident un s, ir de caractere înconjurat de ghilimele: "Popescu, Ionescu"

Intern, compilatorul va aloca o zona de memorie s, i va "scrie" caracterele acolo.

O variabila de tip s, ir de caractere este orice variabila capabila sa stocheze un bloc de memorie de tip char. Pânaacum s, tim cum sa declaram un tablou. Iar un tablou de tip char este un container excelent pentru un s, ir de caractere.Avem alternativa sa nu specificam lungimea tabelului (daca nu intent,ionam sa modificam cont,inutul).

Exemplu char nume[100]; Putem scrie char nume[100] = "Popescu";. În acest exemplu putem accesas, i schimba fiecare element individual, folosind indexarea în tabel: nume[3] = 'E';

Pâna în acest punct, nu exista absolut nici o diferent, a între o bucata de memorie s, i un s, ir de caractere. Diferent,ele sevad când începem sa lucram cu funct,iile din biblioteca standard. Vedem imediat.

Afis, area unui s, ir de caractere se face cu printf folosind specificatorul %s. Exemplu: printf("%s \n",nume); Construct,ia \n este caracterul newline (vedet,i laboratorul)

Citirea unui s, ir de caractere aparent nu este mai complicata decât a unei variabile normale. Dar sunt câteva problememari.

DEOCAMDATA, folosim scanf evident cu acelas, i %s. Zona de memorie trebuie sa fie deja alocata.

Exemplu: scanf("%s", nume) sau scanf("%s", &nume[0]). Variabila nume trebuie sa fie deja declaratas, i alocata.

char sir[100];scanf("%s", sir);

Câteva observat,ii foarte importante:

• scanf cu %s cites, te buffer-ul de tastatura s, i stocheaza caracterele în zona de memorie trimisa ca paramerupâna când se întâlnes, te un caracter alb. Adica spat,iu, tab, newline, end of file.

• scanf NU ARE cum sa valideze câta memorie este disponibila la adresa primita pentru stocare. Aceasta esteo gaura de securitate. Vom vedea la funct,ii ca sunt momente când procesorul cites, te de pe stiva care va fi

28 Chapter 3. Iterat, ia 2

Page 35: Programarea Calculatoarelor - utcluj.rousers.utcluj.ro/~visoft/teach/programareacalculatoarelor.pdfProgramarea Calculatoarelor, Versiune 0.32 1.2Desfas˘, urare 1.2.1Abstractizare

Programarea Calculatoarelor, Versiune 0.32

urmatoarea instruct,iune executata. Cuplat,i asta cu faptul ca de obicei, zona de memorie trimisa ca parametrueste pe stiva s, i obt,inet,i o gaura enorma de securitate. Nu e foarte bine, mai ales când Trinity este la butoane.4

Astea înseamna ca, deocamdata, la laborator putet,i sa citit,i un s, ir de caractere folosind scanf. Pâna vom face intrariies, iri cu fis, iere. Apoi, va trebui sa evitat,i folosirea scanf pentru a citi s, iruri de caractere s, i sa folosit,i metode maisigure.

Recunoas, tet,i operatorul de preluare a adresei s, i observat,i ca pentru prima data am folosit numele unui tabel într-oexpresie fara sa folosim operatorul de indexare. La pointeri vet,i vedea ca numele tabelului este 1:1 cu adresa primuluielement din tablou.

Funct, ii utile care proceseaza s, iruri de caractere

Funct,iile care proceseza o zona de memorie ce respecta convent,ia de s, ir de caractere sunt grupate în bibliotecastrings.h. Aceasta trebuie inclusa la începutul codului sursa, cu #include <string.h>

Fie declarat,ia:

char str[] = "Un sir de caractere";char dest[100];

Funct,iile:

• strlen(str) returneaza un întreg cu numarul de caractere din s, ir

• strcpy(dest, str) copiaza caracterele din str în locat,ia dest s, i returneaza adresa dest. ATENT, IE!Memoria pentru dest trebuie sa fie alocata!

• strcmp(str, dest) compara alfabetic cele doua s, iruri de caractere s, i returneaza un întreg cu semn.Negativ daca cont,inutul de la str apare alfabetic înaintea lui dest sau pozitiv daca este alfabetic, dupa. Zerodaca locat,iile cont, in valori identice.

• stricmp(str, dest) compara tratând la fel literele mici s, i mari.

3.4 Pointeri

Când am dat exemplul cu instruct,iunile în cod mas, ina s, i cu variabiele am spus ca, compilatorul, înlocuies, te automatnumele variabilei cu cod de accesare a zonei ei de memorie. S, i în toate expresiile uzuale, lucram direct cu valoareaaflata acolo, fara sa ne preocupe aducerea valorii în expresie s, i scrierea înapoi a rezultatului.

În funct,ia scanf am vazut ca este nevoie de o adresa de memorie unde codul din aceasta funct,ie sa pastreze rezultatulextras (parsat) din bufferele de tastatura. (eg un întreg sau float).

Am mai vazut ca, la apelul funct,iei, compilatorul copiaza pattern-ul de bit,i ai parametrilor de intrare s, i nu transmitedirect adresa. (din aceasta cauza putem trimite la printf de exemplu, atât variabile cât s, i direct constante).

4 Matrix Reloaded (Warner Bros. Pictures), Trinity foloses, te o astfel de vulnerabilitate pentru a prelua controlul unei ret,ele de calculatoare.

3.4. Pointeri 29

Page 36: Programarea Calculatoarelor - utcluj.rousers.utcluj.ro/~visoft/teach/programareacalculatoarelor.pdfProgramarea Calculatoarelor, Versiune 0.32 1.2Desfas˘, urare 1.2.1Abstractizare

Programarea Calculatoarelor, Versiune 0.32

3.4.1 Declararea pointerilor

Una dintre cele mai puternice facilitat,i ale limbajului C este lucrul direct cu memoria. Avem un tip special, tipulpointer care defines, te o locat,ie de memorie (o variabila) capabila sa stocheze si sa opereze cu adrese de memorie.Pattern-ul de bit,i ce se stocheaza la adresa respectiva va fi interpretat de procesor ca o adresa de memorie.

Exemplu:

int *pi;int i;

În prima linie am definit un pointer spre o zona de memorie ce va putea stoca un întreg. În a 2-a linie am definito variabila capabila sa stocheze un întreg. Un pic despre declararea tipului. Tipul lui pi este int *. Din pacate,operatorul unar * se "lipes, te" de numele variabilei. Declarat,ia int* p1, p2; va declara un pointer (p1) s, i unîntreg p2. Astfel de except,ii de la regula, din pacate, mai sunt. DAR daca avem o singura variabila declarata, regulaca tot ceea ce se afla în stânga numelui este tipul variabilei, se respecta.

Important: Pointerii NU stocheaza niciodata date! Ei stocheaza doar adrese. Daca adresele sunt spre zone dememorie în prealabil rezervate, atunci, putem folosi pointerii în expresii. Altfel, vom avea parte de eroi de execut,iesau de terminarea prematura a programului.

Mai iteram o data declarat,iile de mai sus. Variabila pi are alocata o zona de memorie de 8 octet,i sau sizeof(int*)(presupunând un sistem de operare mai nou, pe 64 bit,i). În funct,ie de locul unde este declarata variabila pi aici estetotul 0 sau un pattern de bit,i fara nici o semnificat,ie pentru program.

Variabila i are alocata o zona de memorie de 4 octet,i sau sizeof(int) unde, la fel, poate fi stocata valoarea 0 sauun pattern de bit,i oarecare.

i = 10 Operat,ie perfect valida, în zona de memorie a variabilei i se stocheaza pattern-ul de bit,i a lui 10.

pi = 10 În funct,ie de versiunea s, i setarile compilatorului aceasta instruct,iune va es, ua la compilare. Daca totus, i esteexecutata, în zona de memorie a lui pi se va stoca adresa 10 (A 11-a locat,ie de memorie, numarând de la adresa 0,vizibila programului nostru). De obicei la as, a adrese mici nu este nimic cu ce ar trebui sa operam.

pi = &i Acum lucrurile încep sa se lege. Operatorul & numit operator adresa sau referent,iere preia adresa rezervatavariabilei i. Operat,ia de atribuire, va copia pattern-ul de bit,i a adresei lui i în locat,ia rezervata variabilei pi. Dinacest moment variabila pi cont,ine o adresa valida, rezervata corect, adresa la s, i cu care putem face operat,ii!

MARE ATENT, IE ÎN ACEST MOMENT AVEM DOUA VARIABILE CU CARE PUTEM OPERA SPRE ACEIAS, IADRESA DE MEMORIE!! Foarte importanta ment,iune s, i care poate duce la bug-uri subtile s, i greu de rezolvat.

Bun, am preluat adresa variabilei, cu ce ma ajuta?

3.4.2 Operat, ii cu pointeri

Operatorul unar de dereferent, iere * ne ajuta sa operam cu cont, inutul zonei de memorie spre care operandulpointeaza.

Astfel, (*pi) = 25 este valid, se va scrie pattern-ul de bit,i în locat,ia spre care pointeaza variabila pi.

Daca executam instruct,iunea anterioara, ce valoare va cont,ine i? Ce se întâmpla daca afis, am i? Încercat,i sa raspundet,iapoi rulat,i:

int *pi;int i;i = 10;pi = &i;

(continues on next page)

30 Chapter 3. Iterat, ia 2

Page 37: Programarea Calculatoarelor - utcluj.rousers.utcluj.ro/~visoft/teach/programareacalculatoarelor.pdfProgramarea Calculatoarelor, Versiune 0.32 1.2Desfas˘, urare 1.2.1Abstractizare

Programarea Calculatoarelor, Versiune 0.32

(continuare din pagina precedenta)

(*pi) = 25;printf("%d ", i);

Am vazut doua operat,ii de baza. Preluarea unei adrese de la o variabila s, i lucrul cu cont,inutul adresei memorate înpointer.

Putem opera însa s, i cu adresa în sine. Din cauza ca adresa de memorie este de fapt un întreg fara semn, operat,iile deadunare s, i scadere sunt valide. Însa nu operam cu întregi ci cu adrese. Aici intervine tipul spre care pointeaza variabilapointer.

Daca avem declarat,ia float *pf; s, i pf pointeaza spre o zona de memorie oarecare, operat,ia pf = pf + 1 vaincrementa adresa din pf cu ATENT, IE sizeof(float)! Din cauza ca am declarat cum ne as, teptam sa interpretampattern-ul de bit,i gasit,i la adresa, compilatorul s, tie ca urmatoarea locat,ie valida este peste 4 octet,i (sau cât estedimensiunea tipului float pe mas, ina respectiva).

La fel funct,ioneaza s, i scaderea unei constante dintr-un pointer.

Atent,ie, rezultatul unei operat,ii dintre un pointer s, i un scalar este tot un pointer! Adica tot o adresa de memorie.

Compilatorul NU face nici un efort în a se asigura ca noua locat,ie este valida s, i/sau rezervata programului. Toataresponsabilitatea revine programatorului.

Operat,ia de egalitate între doi pointeri pi == pj verifica egalitatea adreselor s, i NU a cont,inutului.

Exista o valoare deosebita, si anume adresa NULL (de obicei egala cu 0). Aceasta adresa speciala indica faptul capoinerul nu pointeaza spre ceva valid. NULL este o constanta predefinita în C s, i o putem folosi ca atare:

if (pi != NULL){// pointer valid

}else{// pointer invalid

}

Unele funct,ii care întorc adrese (pointeri) returneaza NULL în caz de es, ec.

3.4.3 Legatura între pointeri s, i tabele

La tablouri am facut afirmat,ia ca numele tabloului, fara un operator de indexare este de fapt adresa primului elementdin tablou. Putem sa gândim ca numele tabloului este de fapt un pointer la primul element. (Sunt câteva diferent,esubtile)

Daca avem declarat,iile:

int tab[30];int *pi;

Atribuirea pi = tab este valida. *pi = 5 va scrie valoarea 5 în primul element din tab, adica la tab[0].

Operat,ia *(i + 3) = 10 va scrie valoarea 10 în locat,ia tab[3] adica la al 4-lea element din tab.

Observat,i ca la expresia *pi = 5 am renunt,at la paranteze (vom vedea ca operatorul unar * este mai prioritar decâtegalitatea) dar am pastrat parantezele în cazul celei de-a doua expresii. Aici, valoarea din paranteze va avea tipuladresa spre int iar valoarea *(i + 3) este ceea ce se afla la acea adresa.

În spate, compilatorul "traduce" indexarea în tabele în operat,ii cu pointeri. De aceea este perfect valid (des, i de multeori incorect) sa scriem tab[-2]

Din cauza ca cele doua operat,ii (operat,iile cu adrese s, i operat,iile cu tabele) se compileaza la fel, putem folosi operatorulde indexare s, i la pointeri: pi[3] = 15 este echivalent cu *(pi + 3) = 15. Înca un exemplu de syntactic sugar.

3.4. Pointeri 31

Page 38: Programarea Calculatoarelor - utcluj.rousers.utcluj.ro/~visoft/teach/programareacalculatoarelor.pdfProgramarea Calculatoarelor, Versiune 0.32 1.2Desfas˘, urare 1.2.1Abstractizare

Programarea Calculatoarelor, Versiune 0.32

Nota importanta. O variabila de tip pointer poate referi o zona de memorie unde avem rezervata o singura locat,ie sauunde avem rezervata mai multe locat,ii pentru a stoca tipul de baza. Vom vedea cum putem aloca bucat,i foarte mari dememorie în heap.

3.4.4 Alocarea de memorie în zona heap

Zona cea mai mare, accesibila programului s, i locat,ia unde trebuie sa stocam datele mari, este heap-ul. Ca s, i orice cet,ine de resursele calculatorului, tot sistemul de operare este cel care gestioneaza aceasta memorie.

Pentru a folosi o zona de memorie, evident trebuie rezervata. Când am terminat de folosit, o dealocam.

Funct,ia malloc() gasita în stdlib.h (STanDard LIBrary) s, i free() fac cele doua operat,ii.

Funct, ia malloc accepta ca parametru un întreg fara semn reprezentând numaru de octet, i dorit a-l rezerva. Va returnao adresa (tip void*) cu zona rezervata. Daca nu se poate rezerva memorie, se returneaza NULL

Exemple:

int *pi;pi = (int*) malloc(sizeof(int) * 1000000);if (pi == NULL){

// Tratez eroarea legata de lipsa memoriei.}// Folosesc zona de memorie alocata, folosindu-ma de variabila pi.// .........free(pi); // Dealoc memoria cand am terminat de lucrat.

Mai sus, am declarat un pointer la o zona de memorie unde intent,ionez sa stochez întregi s, i am solicitat sistemului deoperare o bucata de un milion de întregi. Am înmult,it numarul de întregi dorit cu dimensiunea unui întreg.

Construct,ia din fat,a funct,iei malloc este un operator de conversie care îi spune compilatorului sa converteasca adresagenerala (de tip void*) returnata de funct,ia malloc într-o adresa spre întregi.

Putem opera cu pi ca s, i cum operam cu un tabel. Exemplu pi[233] = 342.

Dupa ce am terminat de procesat, apelam free(pi) pentru a notifica sistemul de operare ca zona de memorie numai este necesara.

Zona alocata nu este init,ializata, ea putând cont,ine orice.

Câteva funct,ii utile pentru lucrul cu memoria:

• memset seteaza toata zona de memorie la o valoare data. As, teapta trei parametri:

– Adresa de memorie

– Valoarea care se va seta (se va trata valoarea ca tipul unsigned char)

– Numarul de octet,i

Exemplu: memset(pi, 0, 1000000). Atent,ie sa NU depas, it,i zona rezervata!

• memcpy copiaza din zona sursa în zona destinat,ie. As, teapta:

– Adresa de destinat,ie

– Adresa sursa

– Numarul de octet,i

Exemplu: memcpy(dest, src, 400). Atent,ie, zona spre care pointeaza dest trebuie dejaalocata. Copierea se face octet cu octet. Daca cumva dest este de alt tip decât src vor aparea

32 Chapter 3. Iterat, ia 2

Page 39: Programarea Calculatoarelor - utcluj.rousers.utcluj.ro/~visoft/teach/programareacalculatoarelor.pdfProgramarea Calculatoarelor, Versiune 0.32 1.2Desfas˘, urare 1.2.1Abstractizare

Programarea Calculatoarelor, Versiune 0.32

bug-uri. Compilatorul nu verifica as, a ca putet,i copia din gres, eala un tabel de int-uri într-o zona încare se pastreaza double. Din nou, un bug subtil s, i greu de identificat.

Observat,i ca la funct,iile de procesare a s, irurilor de caractere nu a trebuit sa specificam câte caractere sa se copieze.Asta deoarece în C, exista convent,ia ca s, irul de caractere se termina cu 0. Funct,iile memcpy funct,ioneaza ignorândcont,inutul pe care opereaza. Lor trebuie sa le specificam câta memorie sa copieze.

La fel cum putem avea pointeri spre int sau float putem avea pointeri spre char care putem sa îi folosim ca s, iruride caractere.

char * str = (char*) malloc(sizeof(char) * 10000);

aloca o zona de memorie pentru un s, ir de 9999 de caractere. De ce nu pentru 10000 caractere?

Un alt tip de pointeri folosit des (vedem urmatoarea iterat,ie) este un pointer la pointeri.

int ** p = (int**) malloc(sizeof(int*) * 100)

rezerva 100 de locat,ii de memorie unde se pot stoca pointeri spre tipul int. Întoarce adresa de început a acestei zone.p[4] va avea tipul int* s, i se va putea stoca acolo o adresa.

Unde sunt utili? La definirea matricilor. p este pointer spre un rând, fiecare element din rând fiind un pointer spre câteo coloana. Operat,ie relativ des întâlnita!

Putem avea pointeri la structuri s, i pointeri la tabele de structuri. Sa vedem un exemplu:

Listing 3.7: Zona de memorie ce stocheaza un vector de structuri

1 // Definitie la nivel global2 typedef struct{3 int i, j, k;4 char nume[30];5 } Informatii;6

7 // undeva intr-o functie8 Informatii *tab_de_informatii; // Am declarat o variabila pe stiva9 //Aloc 500 de structuri de tip Informatii.

10 tab_de_informatii = (Informatii*)malloc(sizeof(Informatii) * 500);11

12 // Operatii13 tab_de_informatii[32].j = 0;14 printf("%s\n", tab_de_informatii[32].nume);15

16 // Curatenie17 free(tab_de_informatii);

Pointeri la structuri, tabele de pointeri la structuri, structuri care cont,in pointeri, iaras, i sunt tipuri compuse foartefolositoare!

Nu ignorat,i aceasta sect,iune! Daca simt,it,i ca avet,i nelamuriri, facet,i exercit,ii, solicitat,i lamuriri suplimentare, cautat,ialte explicat,ii mai bune pe internet. Nu vet,i putea folosi limbajul C daca nu stapânit,i pointerii!

De fapt multe limbaje de programare lucreaza în fundal cu pointeri iar neînt,elegerea lor duce la dificultat,i în a înt,eleges, i opera cu astfel de limbaje (Java, C++, Javascript, Python etc). Programarea obiectuala lucreaza implicit cu pointeri(chiar daca se ocupa limbajul de alocari s, i dealocari).

3.4. Pointeri 33

Page 40: Programarea Calculatoarelor - utcluj.rousers.utcluj.ro/~visoft/teach/programareacalculatoarelor.pdfProgramarea Calculatoarelor, Versiune 0.32 1.2Desfas˘, urare 1.2.1Abstractizare

Programarea Calculatoarelor, Versiune 0.32

3.5 Expresii

Mare parte din instruct,iunile din C sunt formate din expresii care manipuleaza variabile. Acum vom aprofunda multeaspecte legate de expresii.

Formal, o expresie este formata dintr-un operator (CE anume facem? Adunare) s, i operanzi (Pe cine efectuam operat,iade înmult,ire?). Operanzii pot fi variabile, constante, apeluri de funct,ii s, i bineînt,eles, alte expresii.

Operatorii s, i operanzii trebuie sa fie compatibili (vedem mai încolo ce reguli sunt). Dupa efectuarea operat,iei rezultao valoare care are un tip bine definit.

O expresie poate fi implicata ca operand în alta expresie.

Regulile care guveneaza expresiile sunt date de faptul ca procesorul are instruct,iuni specifice pentru fiecare tip de data.Unele convent,ii s-ar putea sa para arbitrare sau non intuitive. Evident, ele sunt o sursa inepuizabila de bug-uri!

Formal, expresiile pot fi unare (au parte de tratament special in C), binare (majoritatea) sau tert,iare (cu 3 termeni).

3.5.1 Operat, ii aritmetice

Începem cu operat,ii simple. Adunari, împart,iri, etc.

• Operatorii unari + s, i -. Expresia -i negativeaza valoarea lui i

• Operatorii binari + , -, * , / s, i %. I-am mai întâlnit, nimic special

• Operatorii logici pe bit, i s, i (and) &, sau (or) |, sau exclusiv (xor) ^, negare (not) ~, deplasare la stânga s, idreapta (left/right bitwise shift) << s, i >>. Ei funct,ioneaza doar pe tipurile întregi

• Operatorul de incrementare s, i decrementare i++ s, i i--, echivalentul lui i = i + 1 respectiv i = i - 1.

În procesor exista doua unitat,i de calcul, unitatea pentru numere întregi reprezentate în Two's complement, numitaALU (Arithmetic Logic Unit) s, i unitatea de calcul în virgula mobila, care, as, teapta numere în format IEEE-754,numita FPU.

Fiecare are instruct,iunile ei specifice de control. Pentru a avea flexibilitate, exista în C reguli de conversie, astfelîncât ambii operanzi sa fie compatibil în momentul efectuarii operat,iei. Regulile de conversie dau s, i tipul final alrezultatului.

Astfel, într-o operat,ie aritmetica (ex împart,irea) în care sunt implicate numere (int, float, char, etc):

0) Operanzii de tip mai mic decât int se convertesc la int.

1) Daca ambii operanzi au acelas, i tip, operat,ia se va efectua de catre unitatea corespunzatoare (ALU sau FPU) iarrezultatul va avea acelas, i tip (char, int, float sau double).

2) Daca sunt doua tipuri diferite, ambii vor fi convertit,i spre tipul cel mai "încapator": char -> int -> float ->double. Rezultatul va avea acest tip mai încapator.

3) Operat,ia dintre un tip fara semn si unul cu semn, va fi de tip fara semn. Asta înseamna ca tipul cu semn se vaconverti la unsigned.

La o prima vedere nu ar fi ceva deosebit. Dar atent,ie mare!

Important: Tipul rezultatului unei operat,ii NU DEPINDE DE VALOAREA REZULTATULUI indiferent de ceoperat,ie este vorba!

Tipul rezultat s, i unitatea care proceseaza operat,ia depinde doar de tipul operanzilor! Haidet,i sa vedem exemple.

34 Chapter 3. Iterat, ia 2

Page 41: Programarea Calculatoarelor - utcluj.rousers.utcluj.ro/~visoft/teach/programareacalculatoarelor.pdfProgramarea Calculatoarelor, Versiune 0.32 1.2Desfas˘, urare 1.2.1Abstractizare

Programarea Calculatoarelor, Versiune 0.32

Listing 3.8: Operat,ii aritmetice

1 5 / 42 5 / 4.03 4 * -104 1.0 * 345 char c1=100, c2=100, c3=100;6 int sc;7 sc = c1 + c2 + c3;8 c1 = c1 + c2 + c3;

În prima linie expresia va avea valoarea 1. NU 1.25!!! Ambii operanzi sunt întregi cu semn, operat,ia va fi efectuatade ALU. Pentru a avea, corect, unitatea FPU implicata, folosit,i un operand de tip float sau double, ca în linia 2.Aici constanta 4.0 este de tip double prin urmare întreaga expresie va fi evaluata la double de catre FPU

Linia trei, nimic surprinzator, arata operatorul unar - care ia precendent, a în fat,a înmult,irii.

Linia 4, rezultatul este 34.0, o constanta de tip double.

Linia 7 va da rezultatul corect, 300, pentru ca tot,i operanzii vor fi convertit,i la int înainte de a fi trimis, i la ALU.Explicabil, regis, trii din procesor de obicei au numar fix de bit,i, la fel si unitatea ALU.

Linia 8 va stoca un rezultat surprinzator. Detalii, la operatorul de atribuire.

Formatul IEEE-754 are precizie limitata. Este important sa ret,inet,i asta, limitele se ating la valori foarte mici sauvalori foarte mari.

Daca avet,i de efectuat o operat,ie, gândit,i-va daca nu o putet,i reformul astfel încât sa nu adunat,i numere foarte mici lanumere foarte mari sau numere foarte mici de multe ori. Rezultatele nu sunt deloc cele as, teptate.

Un exemplu:

3.5. Expresii 35

Page 42: Programarea Calculatoarelor - utcluj.rousers.utcluj.ro/~visoft/teach/programareacalculatoarelor.pdfProgramarea Calculatoarelor, Versiune 0.32 1.2Desfas˘, urare 1.2.1Abstractizare

Programarea Calculatoarelor, Versiune 0.32

Listing 3.9: Adunare de numere mici

#include <stdio.h>int main(){

int count = 1e8;float sum = 0;

for (int i = 0; i < count; i++){sum = sum + 1.0 / count;

}printf("Total count: %f", sum);return 0;

}

Matematic,∑𝑛

𝑖=11𝑛 ar trebui sa dea 1. Variat,i valoarea lui n (count în cazul nostru) sa vedet,i cum variaza rezultatul

final. Pe arhitectura pe care am încercat, pentru 1000 valori rezultatul este 0.9999 iar pentru exemplul de mai sus, 0.25.

O solut,ie eleganta e sa rescriet,i formula matematica evitând astfel de bucle. Adica sum = 1, în cazul nostru. Evident,nu tot timpul este as, a de us, or. Algebra va ajuta aici, sa nu gres, it,i.

3.5.2 Operatorul de asignare sau atribuire

Operatorul de asignare = ia pattern-ul de bit,i din expresia din dreapta egalului s, i îl copiaza în locat,ia din stângaegalului. Face s, i conversii de tipuri, pentru a se potrivi cu tipul destinat,ie. De exemplu, daca vret,i sa scriet,i valoarea456 (un int) într-un double acesta va fi convertit la virgula mobila înainte.

Conversia din float în int se face pierzând din precizie. (ex 12.345 va fi trunchiat la 12).

Linia 8 din Cod 3.8 va genera un rezultat surprinzator, deoarece, compilatorul va încerca sa "înghesuie" o valoaremare într-o locat,ie ce nu poate stoca o asemenea valoare. Rezultatul c1 + c2 + c3 este 300, de tip int. Valoarece necesita 9 bit,i. Locat,ia de memorie c1 are doar 8 bit,i rezervat,i. Pentru a nu mai exploda rachete, compilatorulgenereaza instruct,iuni care copiaza doar bit,ii cei mai put,in semnificativi, din sursa în destinat,ie. Astfel în c1 vomregasi valoarea 44.

Des avem de efectuat o operat,ie s, i de a scrie rezultatul înapoi. Avem o scurtatura: i = i + 2 este echivalent cu i+= 2. Merge pentru operat,iile aritmetice.

Conform definit,iei operatorului = acesta scrie pattern-ul de bit,i în zona de memorie a variabilei din stânga. În stângatrebuie sa avem o expresie care sa poata fi folosita în acest fel. Adica o expresie care sa poata stoca un pattern debit, i. Sa vedem:

int i;i = 10;

Automat, compilatorul va stoca pattern-ul de bit,i a lui 10 la adresa rezervata pentru i. Nu este necesar din parteanoastra sa folosim adrese. Expresia compusa din variabila i este potrivita pentru a stoca pattern-uri de bit,i. Variabilai are un tip, are memorie rezervata, operat,ia se desfas, oara absolut nespectaculos.

În expresia urmatoare avem o sursa de confuzie pentru ca avem s, i pointeri. S, tim ca pentru a lucra cu ceea ce se afla laadresa pointerilor trebuie sa folosim operatorul de dereferent,iere *:

int *pi = &i; // Fac ca pointerul pi sa stocheze adresa lui i

*pi = 20; // Modific ceea se se afla stocat la adresa stocata in pi

Expresia *pi duce la cont,inutul memoriei spre care pointeaza pi. Din nou, expresia *pi = 20; este valida. În*pi putem stoca s, abloane de bit,i. Mare atent,ie! Expresia pi = 20 este s, i ea perfect compilabila! Dar daca înainte

36 Chapter 3. Iterat, ia 2

Page 43: Programarea Calculatoarelor - utcluj.rousers.utcluj.ro/~visoft/teach/programareacalculatoarelor.pdfProgramarea Calculatoarelor, Versiune 0.32 1.2Desfas˘, urare 1.2.1Abstractizare

Programarea Calculatoarelor, Versiune 0.32

stocam valoarea 20 în locat,ia de memorie a lui i (pentru ca pointerul pi a luat adresa lui i), când scriem pi = 20operatorul de asignare va copia pattern-ul de bit,i a lui 20 în zona de memorie a variabilei pi, zona unde se stocheazaadrese. Implicit, pi va pointa spre adresa 20. Sursa de bug-uri!

Un alt exemplu de operat,ie de asignare.:

32 = i;

este invalid. Constanta 32 nu poate fi modificata. Presupunând ca ar avea o zona de memorie, aceasta ar fi stocataîntr-o zona unde este permisa doar citirea.

La fel, atribuirea la un tabel a unei alte adrese, este ilegala:

int tab[10];int *pi = (int*)malloc(100);tab = pi; // Ilegal!

Asta deoarece tab are o adresa fixa ce nu se poate modifica.

Expresiile care se pot utiliza în partea stânga a unei expresii de atribuire se numesc expresii lvalue. Aceasta poate fi:

• Un nume de variabila simpla

• O expresie cu indici (indexare [])

• Un membru a unei structuri (Operatorul .)

• O expresie de pointeri dereferent,iata ( * pointer unde pointer este o expresie ce are valoare s, i tip adresa)

• Un pointer (daca atribuim adrese)

În principiu trebuie sa va gândit,i daca compilatorul are unde stoca pattern-ul de bit,i pe care voi vret,i sa îl atribuit,i. S, idaca variabilele care manipuleaza zona respectiva interpreteaza în modul în care va as, teptat,i, zona de memorie.

O alta metoda de a "gândi" acest lvalue este ca el trebuie sa aiba o zona de memorie rezervata s, i ca, valoarea lui,daca e folosita în stânga, este valoarea memorata la acea adresa. Operatorul =, pentru lvalue face cumva transparent ooperat,ie de referent,iere la toata expresia din stânga, ca sa s, tie unde sa scrie rezultatul final.

Luat,i care explicat,ie empirica vi se pare mai utila, ideea e sa s, tit,i care e deosebirea între pi = expresies, i *pi = alta_expresie. Sau, de ce 42 = raspunsul_la_toate nu compileaza chiar daca s, timraspunsul_la_toate.5

3.5.3 Operatori de comparat, ie s, i logici

Avem:

• Operatorii logici s, i &&, sau ||, negare !.

• Operatorii de comparat,ie mai mic <, mai mic sau egal <=, egal ==, mai mare sau egal >=, mai mare >, diferit!=.

Valoarea expresiei este zero pentru fals s, i non zero (probabil 1) pentru adevarat.

Atent,ie la compararea de tipuri double sau float! Expresia 2.0 / 3.0 == 1.33333 este FALSA! Pentru ca,matematic, 2/3 este un rat,ional cu un numar infinit de zecimale care NU poate fi reprezentat perfect în virgula mobila.Rezultatul expresiei 2.0 / 3.0 are pattern de bit,i us, or diferit de constanta de tip double cu valoarea 1.3333.

Ce solut,ie se aplica în cazul numerelor în virgula mobila este verificarea daca diferent,a absoluta între operanzi estemai mica decât un epsilon. Cautat,i în biblioteca standard math.h funct,iile modul (abs s, i fabs).

5 The Hitchhiker's Guide to the Galaxy (Buena Vista Pictures Distribution).

3.5. Expresii 37

Page 44: Programarea Calculatoarelor - utcluj.rousers.utcluj.ro/~visoft/teach/programareacalculatoarelor.pdfProgramarea Calculatoarelor, Versiune 0.32 1.2Desfas˘, urare 1.2.1Abstractizare

Programarea Calculatoarelor, Versiune 0.32

Comparat,iile între float s, i int funct,ioneaza pentru ca ambele tipuri sunt convertite la double înainte decomparat,ie.

Atent,ie s, i la pointeri, compararea a doi pointeri se rezuma la a compara ADRESELE s, i nu cont,inutul. Folosit,i strcmppentru a compara doua s, iruri. Folosit,i comparat,ie element cu element pentru vectori de numere.

Atent,ie s, i la diferent,a între operat,iile logice pe bit, i s, i cele logice:

5 & 10 == 0 // Operatie PE BITI! 0101 & 1010 == 00005 && 10 == 1 // Operatie LOGICA, ambele valori sunt diferite de zero, rezultatul este→˓True

Conceptul de scurt circuit

Presupunem ca avem expresia a && b iar în momentul evaluarii, expresia a este 0 (False). Execut,ia expresiei va saripeste evaluarea lui b pentru ca deja se cunoas, te valoarea de adevar a întregii expresii.

În cazul în care expresiile sunt simple, acest lucru poate fi privit ca o optimizare. DAR ATENT, IE! Daca evaluarea luib presupune evaluarea unei funct,ii, aceasta funct,ie NU VA MAI FI APELATA cât timp expresia a este falsa.

Mecanismul de short circuit este foarte util când de exemplu, vrem sa verificam daca un pointer nu este NULL s, i dacanu este NULL, sa verificam daca un element are o anumita valoare.

Exemplu:

Listing 3.10: Short circuit în operatorii logici

int main(){

int* pi=NULL;int a;a = ((pi != NULL) && (pi[4] == 42)); // Executie fara nici o problemaa = ((pi[4] == 42) && (pi != NULL)); // Sistemul de operare omoara programulreturn 0;

}

Prima atribuire a lui a funct,ioneaza fara probleme pentru ca, expresia de dupa = va fi falsa dupa evaluarea primeicomparat,ii. Nu se va mai continua evaluarea celui de-al doilea operand.

A doua atribuire a lui a, des, i IDENTICA din punct de vedere matematic, es, ueaza6.

Un alt exemplu de comportament neintuitiv ar fi daca în a doua expresie facet,i un apel de funct,ie, apel de funct,ie careinteract,ioneaza cu mediul extern ei (afis, eaza ceva pe ecran sau cites, te din fis, ier). Evitat, astfel de apeluri de funct,ie, înexpresii compuse.

Cam singurul scenariu recomandat este acela în care evaluarile celorlalt,i operanzi depind de succesul operanziloranteriori: Exemplu: A && B && C && D s, i execut,ia independenta a lui C poate fi ilegala, daca condit,iile A sau Bsunt false. Atunci, rearanjat,i codul astfel încât operat,iile dependente sa fie DUPA cele independente. La fel ca s, i laexemplul cu pointerul NULL

6 Pe linux, sistemul operare detecteaza accesul în zone interzise. Pe mas, inile Windows, experient,a poate varia. S-ar putea ca acest cod sa seexecute fara nici o problema. Nu este din cauza ca nu exista short-circuit ci din cauza diferent,elor de OS.

38 Chapter 3. Iterat, ia 2

Page 45: Programarea Calculatoarelor - utcluj.rousers.utcluj.ro/~visoft/teach/programareacalculatoarelor.pdfProgramarea Calculatoarelor, Versiune 0.32 1.2Desfas˘, urare 1.2.1Abstractizare

Programarea Calculatoarelor, Versiune 0.32

3.5.4 Alt, i operatori

Enumeram sumar, cât,iva operatori neacoperit,i pâna acum.

Accesul la un element dintr-o structura

Fie:

typedef struct {int a, b;

} Structura;

// intr-o functie

Structura s, *ps;s.a = 10; // Acces la elementul a din variabila s de tip structuraps = &s;(*ps).a = 20; // Acces prin intermediul pointerului psps->a = 30; // Syntax sugaring pentru accesul la elementele structurii, folosind→˓pointeri

Operatorul de fort,are a tipului

Denumit s, i operatorul de cast. Acesta fort,eaza compilatorul sa interpreteze pattern-ul de bit,i ca tipul din paranteze:

char *c;c = (char*) malloc(sizeof(char) * 100);

Aici rezultatul de tip void*, returnat de funct,ia malloc este convertit la char*.

Alt exemplu:

int a;float f;a = (int)f;

Aici convertesc explicit valoarea lui f la tipul int înainte de atribuire.

Operatorul condit, ional

Expresia E1 ? E2 : E3 are valoarea E2 daca expresia E1 este adevarata, altfel are valoarea E3.

Folosit, rar aceasta forma. Nu aduce claritate mare codului, mai ales daca expresiile sunt lungi.

3.5.5 Prioritat, ile s, i asocierea operatorilor

La fel cum adunarea s, i înmult,irea din matematica au regulile lor de prioritate s, i asociere, as, a este s, i cu operat,iile dinC.

S, i acest subiect este sursa mare de bug-uri. În general aceste reguli de prioritate s, i asociere ar trebui s, tiute cam pe derost. Multe sunt intuitive (înmult,irea e prioritara fat, a de adunare) dar nu toate.

3.5. Expresii 39

Page 46: Programarea Calculatoarelor - utcluj.rousers.utcluj.ro/~visoft/teach/programareacalculatoarelor.pdfProgramarea Calculatoarelor, Versiune 0.32 1.2Desfas˘, urare 1.2.1Abstractizare

Programarea Calculatoarelor, Versiune 0.32

Table3.1: Operatori s, i prioritat,i. Ordinea descrescatoare a prioritat,iloreste de sus în jos. Regula de asociere pe un rând este data în dreapta.Operatorii sunt despart,it,i prin spat,iu. Virgula , este un operator valid!Între paranteze am scris câteva lamuriri.

Operatori Asociere() (Apel de funct,ie, grupare expresii) [] . -> Stânga la dreapta+ (unar) - (negativare) & (referint, a) *(dereferent,iere) ++ -- (tip) (conversie de tip) ! ~ Dreapta la stânga* (înmult,ire) / % Stânga la dreapta+ (binar) - (binar) Stânga la dreapta<< >> Stânga la dreapta< <= >= > Dreapta la stânga== != Dreapta la stânga& (binar, s, i pe bit,i) Stânga la dreapta^ Stânga la dreapta| Stânga la dreapta&& Stânga la dreapta|| Stânga la dreapta? : Stânga la dreapta= <<= >>= += -= *= /= %= &= ^= |= Dreapta la stânga, Stânga la dreapta

Fie codul de mai jos:

Listing 3.11: Exemplu de prioritat,i ale operatorilor

1 #include <stdio.h>2

3 typedef struct {4 int *v1;5 } Structura;6

7 int main()8 {9 Structura *s;

10 s = (Structura*)malloc(sizeof(Structura));11 s->v1 = (int*)malloc(sizeof(int) * 5);12 s->v1[0] = 45;13

14 int k;15 k = 10 * *s->v1;16 printf("k=%d", k);17 return 0;18 }

Gres, eli frecvente se fac de obicei la lucrul cu pointerii. Avem mai mult,i operatori care lucreaza cu indecs, i, cu referint,e,operatori care din pacate se suprapun cu cei binari aritmetici.

La linia 12 avem un operator de indexare în tabel s, i de acces la membri. Conform Tabelul 3.1 operatorul -> este la felde prioritar ca s, i []. Din cauza regulii de asociere, expresia se va evalua de la stânga la dreapta. Astfel, mai întâi seevalueaza s->v1 ca fiind un pointer la int (declarat,ia din linia 4). Apoi se indexeaza în aceasta zona de memorie,luând primul element.

Linia 12 este uzuala, astfel de construct,ie putând s, i voi sa folosit,i.

Linia 15 este un pic mai complicata. Prima * este operatorul binar de înmult,ire, a 2-a * este operatorul unar dereferent,iere. Apoi vine construct,ia s->v1 care este cea mai prioritara din expresie. Des, i sintactic corect NU va

40 Chapter 3. Iterat, ia 2

Page 47: Programarea Calculatoarelor - utcluj.rousers.utcluj.ro/~visoft/teach/programareacalculatoarelor.pdfProgramarea Calculatoarelor, Versiune 0.32 1.2Desfas˘, urare 1.2.1Abstractizare

Programarea Calculatoarelor, Versiune 0.32

recomand în practica sa scriet,i as, a ceva. Folosit,i paranteze pentru claritate: k = 10 * (*s->v1): Mai clar, dincauza echivalent,ei tabele - pointeri: k = 10 * s->v1[0]

Prioritatea operatorilor este sursa de bug-uri. Nu am alt sfat decât folosirea parantezelor (la dubii) s, i învat,atul pe derost macar a câtorva rânduri din tabel (ex cele cu operatorii de indexare, unari, logici s, i de atribuire)

3.5.6 Supraîncarcarea operatorilor

La liceu, mult,i scriat,i construct,ii de genul: cout << i << endl s, i va as, teptat,i corect, sa scrie pe ecran cont,inutulvariabilei i. Limbajul era C++. Operatorul << este tot operatorul de deplasare la dreapta pe bit,i, are aceeas, i prioritate,aceleas, i reguli de asociere ca s, i în C.

Limbajele orientate pe obiect accepta operat,ia de supraîncarcare. Aceasta operat,ie permite definirea mai multorblocuri de cod pentru acelas, i nume funct,ie. Compilatorul va s, ti care bloc de cod va fi apelat, uitându-se laparametri. Primul parametru din expresie este cout, un obiect de tip ostream. Cheia de bolta este tipul obiectului.Compilatorul cauta daca exista o funct,ie care sa supraâncarce operatorul << s, i care sa accepte primul parametru unobiect de tip ostream. Gases, te funct,ia s, i o apeleaza. (Daca nu, va returna un mesaj de eroare)

Mecanismele s, i regulile de supraîncarcare s, i suprascriere sunt foarte puternice, permit scrierea de cod elegant DARevident, ca orice lucru puternic, la început, sunt o sursa mare de erori de compilare s, i frustrare.

Limbajul C NU permite operat,iile supraîncarare s, i suprascriere.

3.6 Funct, ii

În Iterat,ia 1 am vazut ca funt,iile sunt grupari de instruct,iuni. Acum intram mai adânc în anatomia unei funct,ii s, i vedemcum putem sa definim funct,ii.

Sa luam de exemplu o funct,ie trigonometica. Sinus. Matematic, daca vrem sa aflam 𝑠𝑖𝑛(𝑥) pentru un x oarecareputem scrie o serie Taylor s, i ne apucam de calcule. Codul este mai complicat, sunt metode mai eficiente matematic,trebuie verificate tot felul de condit,ii, într-un cuvânt, nu vrei sa tot scrii serii Taylor de fiecare data când trebuie calculatun sin(x).

Ar fi frumos daca am putea grupa instruct,iunile acestea într-o funct,ie (funct,ie == construct,ie a limbajului C, s, i nufunct,ie matematica) s, i sa o apelam de fiecare data când vrem sin(x). Trimitem valoarea pentru care dorim evaluareaiar funct,ia returneaza rezultatul.

În biblioteca standard math.h gasim multe funct,ii matematice utile. Radicali, exponent, funct,ii trigonometrice,logaritmi, etc. Ca sa lucram cu astfel de funct,ii, scriem:

// importam definitiile fuctiilor matematice#include <math.h>

// intr-o functiefloat a, b;a = 3.1415 / 2;b = sin(a);

Linia maracta apeleaza funct,ia sin cu parametrul x s, i stocheaza rezultatul în variabila b.

Daca vrem sa definim funct,ii (element esent,ial al programarii imperative) scriem as, a:

tipul_returnat numele_functiei ( tip1 parametru1, tip2 parametru 2 )bloc_de_instructiuni

3.6. Funct, ii 41

Page 48: Programarea Calculatoarelor - utcluj.rousers.utcluj.ro/~visoft/teach/programareacalculatoarelor.pdfProgramarea Calculatoarelor, Versiune 0.32 1.2Desfas˘, urare 1.2.1Abstractizare

Programarea Calculatoarelor, Versiune 0.32

Construct,ia tip1 parametru1, tip2 parametru 2 din interiorul parameterzelor este numita listaparametrilor formali. Parametrii formali sunt alocat,i pe stiva înainte de a intra în corpul funct,iei. Lista poate sacont,ina zero sau mai mult,i parametri, separat,i prin operatorul virgula ,.

Daca funct,ia nu returneaza nici o valoare, ea va avea tipul void. Sub nici o forma nu trebuie sa lipseasca declarat,iatipului returnat.

Daca funct,ia returneaza ceva (tip non void) atunci undeva, în corpul funct,iei trebuie sa existe instruct,iunea returnexpresie;. Expresia expresie trebuie sa fie de tip compatibil cu tipul_returnat. Instruct,iunea returnva s, i instrui procesorul sa elibereze memoria variabilelor formale s, i locale iar urmatoarea instruct,iune executata sa fiecea de dupa apelul de funct,ie (ex sa evalueze în continuare expresia de unde a fost apelata funct,ia).

Daca tipul funct,iei e void dar totus, i vrem sa ies, im din funct,ie (ex într-un if) scriem return;.

Elementele funct,iei (tipul, numele, lista parametrilor formali) este numit antetul funt,iei. Instruct,iunea compusa dedupa, se numes, te corpul funct,iei.

Putem sa declaram o funct,ie, fara sa îi specificam corpul. În cazul acesta, scriem antetul urmat de ;. Acesta construct,iese numes, te prototipul funct, iei. Este singurul lucru necesar pentru compilator pentru a s, ti cum sa apeleze funct,ia.Deasemenea, de obicei, doar prototipul este specificat în documentat,ie.

Important: În documentat,ii gasim prototipul funct,iei s, i nu toata declarat,ia. Declarat,ia poate exista în alte fis, ieresau în forma direct compilata.

Exemplu de funct,ie:

1 int increment(int i){2 int j;3 j = i + 10;4 return j;5 }

Tipul returnat de funct,ie este int. Funct,ia se numes, te incremenet s, i are un parametru, numit în interioruldeclarat,iei funct,iei, i. Acesta are tipul int. La apel, aici va fi copiata valoarea cu care aceasta funct,ie este apelata.

La apelul funct,iei, trebuie sa scriem o valoare (sau variabila) compatibila cu tipul parametrului formal corespunzator:increment(23) sau increment(k). Valorile cu care se apeleaza o funct,ie se numesc parametri reali sau efectiviai funct,iei.

Compilatorul va lua pattern-ul de bit,i stocat în locat,iile parametrilor efectivi s, i îl va scrie în stiva, la adresele alocatepentru parametrii formali. Acest lucru se numes, te apel prin valoare sau transmiterea prin valoare a parametrilor.

Cuplând acest lucru cu vizibilitatea variabilelor, putem observa ca, daca funct,ia vrea sa modifice valoarea lui i, poatesa o faca dar modificarea "va disparea" odata cu ies, irea din funct,ie.

Alt exemplu mai complicat:

Listing 3.12: Definit,ii de funct,ii. Apel prin valoare. Vizibilitateavariabilelor locale

1 int increment(int i){2 i += 10;3 return i;4 }5 int main(){6 int i, j; // DIFERIT fata de i-ul alocat in functia increment7 i = 42;8 j = increment(i);

(continues on next page)

42 Chapter 3. Iterat, ia 2

Page 49: Programarea Calculatoarelor - utcluj.rousers.utcluj.ro/~visoft/teach/programareacalculatoarelor.pdfProgramarea Calculatoarelor, Versiune 0.32 1.2Desfas˘, urare 1.2.1Abstractizare

Programarea Calculatoarelor, Versiune 0.32

(continuare din pagina precedenta)

9 printf("%d %d\n", i, j);10

11 j = increment(-100);12 printf("%d %d\n", i, j);13 return 0;14 }

Ce valori credet,i ca se afis, eaza? Observat,i ca valoarea lui i din corpul funct,iei main se modifica o singura data, lalinia 7. i-ul din linia 6 este alocat pe stiva, s, i este dealocat abia la ies, irea din funct,ia main.

Variabila i din linia 1 este alocata pe stiva în momentul apelului s, i nu are nimic de a face cu variabila din linia 6, cuacelas, i nume. Doar o "coincident, a" de nume.

Acest lucru ne ajuta foarte mult când scriem cod s, i folosim indecs, i i, j, k ca sa iteram. Nu ne intereseaza dacasunt folosit,i sau nu în alte funct,ii. La fel, când scriem codul unei funct,ii, ne concentram doar pe variabilele delcaratelocal.

La evaluarea expresiei din linia 8, funct,ia va returna valoarea 52, de tipul int, tipul declarat al funct,iei.

Important: Funct,iile care nu modifica memoria (alta decât cea alocata pentru variabilelel locale s, i formale), nuacceseaza I/O, nu aduc alte modificari ale variabilelor din exterior, se numesc funct,ii fara efecte secundare sau funct,iipure. Acest concept este esent,ial pentru programarea funct,ionala.

Folosit,i cât mai des funct,ii fara efecte secundare, care interact,ioneaza cu mediul doar prin valoarea returnata.

Grupat,i instruct,iunile cu efecte secundare (afis, are, citire de la tastatura/fis, ier) în funct,ii separate de cele care facprocesari a datelor.

Acest concept de separare a responsabilitat,ilor este important în programare. Gândit,i-va ce greu at,i interpreta expresiadin linia 8 daca funct,ia (numita sugestiv increment) se apuca sa mai s, i stearga fis, iere aiurea de pe disk sau sa alocezone largi de memorie. Oare nu at,i fi curios, i cine a scris un astfel de cod?7. Mai ales daca at,i petrecut dupamese lungiîncerând sa depanat,i un bug legat de funct,ia increment?

Majoritatea funct,iilor din math.h sunt funct,ii fara efecte secundare. Daca mai adaugam s, i faptul ca funct,ia estes, i determinista s, i pura atunci, putem stoca valorile funct,iei (pentru ca valoarea de ies, ire depinde determinist doarde valoarea de intrare) iar la apeluri ulterioare, daca parametrii sunt identici, nu mai apelam codul s, i returnamdirect valoarea memorata anterior. Acest lucru este exploatat foarte mult de paradigma programarii funct,ionale s, ifac implementarea de algoritmi sofisticat,i, banala (gen cautari în structuri de date sau programare dinamica).

3.6.1 Apel prin referint, a

Bun, programarea funct,ionala are avantajele ei clare, dar ce se întâmpla daca vrem sa modificam valorile trimise caparametru?

Haidet,i sa adaugam funct,ionalitate la codul anterior. Îl rescriem, astfel încât funct,ia increment sa accepte un tabloude întregi s, i sa îi incrementeze pe tot,i, fara sa aloce memorie noua. Despre o astfel de operat,ie (de obicei matematica),care modifica la fel mai multe valori (de obicei stocate într-un tabel) se zice ca se efectueaza "pe loc", in place.

7 Exista diverse instrumente de gestionare a codului sursa. Unul dintre ele este git. Un (sub) instrument este git blame. Dupa cât desugestiv este numele comenzii, git blame spune cine, la ce data s, i cu ce scop a modificat ultima data (sau în punctul dorit al "istoriei" codului)fiecare linie din fis, ier.

3.6. Funct, ii 43

Page 50: Programarea Calculatoarelor - utcluj.rousers.utcluj.ro/~visoft/teach/programareacalculatoarelor.pdfProgramarea Calculatoarelor, Versiune 0.32 1.2Desfas˘, urare 1.2.1Abstractizare

Programarea Calculatoarelor, Versiune 0.32

Listing 3.13: Operat,ii in place. Funct,ia este considerata cu efectesecundare.

1 #include <stdio.h>2 void increment(int *tab, int n){3 for(int i = 0; i < n; i++){4 tab[i] += 10;5 }6 }7 void afiseaza(int *tab, int n){8 for(int i = 0; i < n; i++){9 printf("%d ", tab[i]);

10 }11 printf("\n");12 }13 int main(){14 int tabel[] = {1, 2, 3, 4, 5};15 afiseaza(tabel, 5);16 increment(tabel, 5);17 afiseaza(tabel, 5);18 return 0;19 }

Observat,i ca cea ce se scrie pe ecran ca urmare a instruct,iunii din linia 15 este diferit de ceea ce se scrie la instruct,iunea17. Diferent,a este cont,inutul variabilei tabel.

Important: Grupari de parametri formali de genul int *tab, int n vet,i întâlni des în lucrul cu tabele sau cuzone de memorie. Dupa cum spuneam, funct,iile nu au cum sa s, tie câta memorie avem rezervata la adresa tab. Trebuiesa spunem explicit, transmit,ând numarul de elemente ca valoare separata.

Am vazut cum se modifica un tabel. Dar daca vrem sa modificam o singura valoare? Cum face scanf acest lucru?

Parametrul formal al funct,iei accepta o adresa (la fel ca la tabel) dar ca parametru real, trimitem adresa variabilei pecare vrem sa o modificam, adresa pe care o gasim cu operatorul &.

Listing 3.14: Apel prin referint, a.

1 #include <stdio.h>2 void increment(int *i){3 *i += 10;4 }5 int main(){6 int i;7 i = 42;8 increment(&i); // Operatorul de referentiere. Trimitem adresa lui i.9 printf("%d\n", i);

10

11 increment(-100); // De ce crapa?12 printf("%d\n", i);13 return 0;14 }

Apelul de la linia 8 din codul anterior este similar celui de la scanf. Am trimis o adresa ca parametru. Observat,ica linia 11 ridica semne de întrebare la compilare iar la execut,ie codul es, ueaza. De ce? Pentru ca, pattern-ul de bit,i avalorii întreagi -100, în Two's complement va fi interpretat ca o adresa de memorie. Evident ca acolo nu avemnimic rezervat. As, a ca sistemul de operare detecteaza accesul ilegal s, i evacueaza programul din memorie.

44 Chapter 3. Iterat, ia 2

Page 51: Programarea Calculatoarelor - utcluj.rousers.utcluj.ro/~visoft/teach/programareacalculatoarelor.pdfProgramarea Calculatoarelor, Versiune 0.32 1.2Desfas˘, urare 1.2.1Abstractizare

Programarea Calculatoarelor, Versiune 0.32

Des, i în limbajul C exista DOAR apel prin valoare (implicit compilatorul copiaza pattern-ul de bit,i ai parametrilor realiîn memoria parametrilor formali) folosind construct,ia anterioara putem spune ca efectuam apel prin referint, a.

Exista limbaje unde implicit apelul este prin referint, a (FORTRAN, Java, Python), acolo, variabila formala are accesDIRECT la zona de memorie a variabilei reale. Foarte importanta distinct,ia! Deasemenea, în C++ putem declara tipulformal cu operatorul & s, i transmiterea se va face automat prin referint, a.

3.6.2 Stiva

Procesorul are un registru special care îi spune care va fi urmatoarea linie de cod ce va fi executata. Registrul senumes, te de obicei PC (Program Counter). Funct,iile de control (if, for) manipuleaza acest registru.

Funct,ia nu este doar o grupare de linii de cod, apelabile comod s, i cu parametri. Exista instruct,iuni speciale înprocesor care executa apelul de funct,ie, manipulând specific registrul PC. Aceste instruct,iuni salveaza pe stiva valoarearegistrului PC dupa care încarca în PC adresa de începere a instruct,iunilor funct,iei. Compilatorul adauga bineînt,elescod de alocare a parametrilor formali, copiere a valorilor parametrilor reali, etc.

La executarea instruct,iunii return procesorul cites, te de pe stiva valoarea curenta s, i o încarca în PC. Urmatoareainstruct,iune va fi executata la adresa citita anterior de pe stiva.

Acest mecanism permite ca procesorul sa s, tie unde sa se întoarca la terminarea execut,iei codului dintr-o funct,ie(exemplu printf). La fel, mecanismul funct,ioneaza s, i la apelul aceleias, i funct,ii de mai multe ori.

De obicei avem un apel înlant,uit de funct,ii: main() apeleaza f1() care apeleaza printf(). Dupa ce se terminaapelul ultimei funct,ii apelate, printf() vrem sa ne întoarcem la funct,ia anterioara, adica f1(). Când se terminaexecut,ia lui f1() vrem sa continuam cu main(). Din main() mai apelam printf(). Ne as, teptam ca execut,iasa continue tot în main() s, i nu în f1().

Având în vedere ca atât parametrii formali, variabilele automatice cât s, i adresa de unde trebuie sa se continue execut,iaprogramului sunt stocate în acelas, i loc, pe stiva, orice modificare aiurea a memoriei de pe stiva are consecint,e gravepentru viat,a programului. Vom vede în Iterat,ia III cum se poate exploata acest lucru pentru a prelua (ilicit) controluldispozitivului.

3.6.3 Apel recursiv

Intram foarte pe scurt în recursivitate.

Având în vedere ca adresa de început a instruct,iunilor funct,iei este cunoscuta în momentul executarii funct,iei esteperfect valid sa apelez funct,ia curenta din funct,ia curenta. Adica apel recursiv.

Codul de alocare a variabilelor se executa la intrarea în funct,ie, as, a ca variabilele (formale s, i locale) sunt independentede apelul anterior s, i independente de apelurile urmatoare. (pentru ca vor fi alocate în locat,ii diferite pe stiva)

E de ret,inut sa evitat,i construct,iile recursive. Daca totus, i folosit,i recursivitatea, primul lucru care îl facet,i în funct,ieeste sa verificat,i condit,ia de ies, ire nerecursiva din funct,ie. Apoi, sa scriet,i codul care efectueaza apelul recursiv. Lipsacondit,iei de oprire este a doua mare cauza de erori de depas, ire a stivei.

Vom vedea în iterat,ia 3 cum putem evita recursivitatea s, i cum o folosim corect (câteodata este modul cel mai us, or dea rezolva o problema).

3.6. Funct, ii 45

Page 52: Programarea Calculatoarelor - utcluj.rousers.utcluj.ro/~visoft/teach/programareacalculatoarelor.pdfProgramarea Calculatoarelor, Versiune 0.32 1.2Desfas˘, urare 1.2.1Abstractizare

Programarea Calculatoarelor, Versiune 0.32

3.7 Intrari/ies, iri

Pe lânga tastatura s, i ecran, un microprocesor mai are acces la multe alte periferice. Disc (memoria non volatila),interfat,a cu ret,eaua, o camera video, un microfon, etc.

Programul, când interact,ioneaza cu mediul extern o face prin intermediul sistemului de operare. Acesta îi pune ladispozit,ie diverse proceduri (endpoints) cu care poate sa citeasca s, i sa scrie bucat,i de date catre s, i dinspre acesteperiferice.

Multe dintre acestea pot fi unificate în C sub forma funct,iilor de lucru cu fis, ierele.

Aceste funct,ii, fiind dependente de OS, sunt de obicei gata implementate în toolchain, noi doar trebuind sa le apelam.

Majoritatea intrarilor s, i ies, irilor au un buffer atas, at, adica o zona de memorie vazuta atât de sistemul de operare cât s, ide program. De aceea, daca s, tim sa lucram cu fis, ierele vom învat,a rapid sa lucram cu astfel de periferice. Va trebui sacitim documentat,ia perifericului respectiv.

Sunt doua nivele de lucru cu fis, ierele în C, noi ne axam aici pe nivelul superior.

Ca sa lucram cu un fis, ier, trebuie sa îi spunem sistemului de operare ca vrem sa citim dintr-un fis, ier. OS-ul verificadaca se poate (exista fis, ierul), daca avem drepturi de citire/scriere, iar daca totul este ok, sistemul de operare nereturneaza nis, te informat,ii specifice, unice pentru acel fis, ier. Pâna la încetarea lucrului cu fis, ierul, va trebui sa pastraminformat,iile s, i sa i le trimitem de fiecare data când interact,ionam cu respectivul fis, ier.

În C, aceste informat,ii se pastreaza într-o structura de tip FILE. Gândit,i-va la un cookie din browser.8.

Când efectuam operat,ii cu orice periferic, trecem prin sistemul de operare. Când init,ializam perifericul, sistemulde operare ne aloca nis, te resurse, pentru a putea opera cu dispozitivul. Ca orice resursa din calculator, acestea suntpartajate cu alte programe. De aceea, dupa ce ne-am facut treaba cu perifericul trebuie sa informam sistemul deoperare. Acesta va elibera aceste resurse. Daca nu, este posibil sa sufere s, i alte programe. Nu odata at,i auzit expresia"Soft-ul asta manânca prea multe resurse s, i îmi încetines, te calculatorul chiar daca nu face mai nimic". Folosirea aiureas, i fara cap a resurselor duce la un astfel de comportament.

3.7.1 Deschiderea unui fis, ier

Funct,iile de lucru cu fis, iere se gasesc în stdio.h.

Deschiderea unui fis, ier se face cu fopen (File OPEN). Aceasta va returna un pointer spre o structura de tip FILE.

Antetul funct,iei este:

FILE * fopen ( const char * path, const char * mode )

Aici, path defines, te calea spre fis, ier (absoluta sau relativa, atent,ie, calea este dependenta de OS).

S, irul de caractere mode descrie sistemului de operare cum vrem sa folosim fis, ierul. Valori uzuale: "r" pentru citire,"w" pentru scriere, "a" pentru adaugare la sfârs, itul fis, ierului.

Implicit, fis, ierul este deschis în mod text. Pentru lucru cu informat,ie binara, adaugat,i un b în textul variabilei mode.

Daca fis, ierul nu exista, s, i este deschis în mod w sau a acesta este creat. Daca exista, s, i se deschide cu w, cont,inutuleste s, ters. Bug frecvent.

Funct,ia returneaza NULL în caz de es, ec. O buna practica este de a avea un if imediat dupa fopen pentru a verificadaca deschiderea s-a facut cu succes.

Fiecare fis, ier deschis are o pozit,ie curenta. La aceasta pozit,ie se efectueaza toate operat,iile. O operat,ie de scriere saucitire muta pozit,ia cu câte elemente s-au citit sau s-au scris. Daca fis, ierul este deschis în mod r sau w acest index arevaloarea 0 la început. Daca este deschis în modul a va avea o valoare dupa ultimul element.

8 Nu este o problema daca nu s, tit,i ce înseamna asta.

46 Chapter 3. Iterat, ia 2

Page 53: Programarea Calculatoarelor - utcluj.rousers.utcluj.ro/~visoft/teach/programareacalculatoarelor.pdfProgramarea Calculatoarelor, Versiune 0.32 1.2Desfas˘, urare 1.2.1Abstractizare

Programarea Calculatoarelor, Versiune 0.32

"Ultimul" caracter dintr-un fis, ier este valoarea speciala EOF (end of file). Aceasta va fi pusa în buffer atunci cândcitim dincolo de sfârs, itul fis, ierului.

Important: Fiecare structura de tip FILE are indexul ei specific. Daca un fis, ier este deschis de mai multe ori (ex demai multe programe odata) fiecare va avea structura FILE corespunzatoare (sau echivalentul în limbajul respectiv deprogramare) Managementul acestor indecs, i precum s, i a conflictelor de scriere/citire se realizeaza de catre sistemul deoperare.

Exista cât,iva pointeri de tip FILE deja deschis, i, când pornes, te programul. Cât,iva mai utili:

• stdin este buffer-ul de intrare standard.

• stdout este buffer-ul de ies, ire standard.

• stderr este ies, irea de eroare standard.

Funct,iile printf s, i scanf lucreaza automat cu ele.

Dupa ce am terminat de lucru cu un fis, ier, evident, închidem:

int fclose(FILE*f)

Funct,ia returneaza 0 la închiderea normala.

Important: Ca orice detaliu care t,ine de API-ul limbajului aceste funct,ii nu trebuie învat,ate pe de rost. Se poateîntâmpla ca dupa zeci de ani de lucru înca sa mai încurcat,i ordinea parametrilor sau semnificat,ia valorii returnate. Maiales dupa ce învat,at,i s, i alte limbaje de programare.

3.7.2 Accesul la nivel de caracter

Cea mai mica unitate de schimb cu un fis, ier este un caracter. Avem funct,iile perechi:

int getc(FILE*f) s, i int putc(int c, FILE*f)

getc returneaza codul ASCII a caracterului citit s, i avanseaza indexul fis, ierului cu o unitate. putc scrie codul ASCIIla pozit,ia curenta.

3.7.3 Accesul la nivel de s, ir de caractere

Funct,ia de baza pentru citire este:

char *fgets(char* s, int n, FILE *f)

Funct,ia accepta un pointer spre o zona de memorie DEJA ALOCATA s, i va citi maxim n-1 caractere în acea zona dememorie. f este un pointer spre FILE, returnat de fopen.

De obicei funct,ia returneaza adresa pointerului s. Daca întâlnes, te direct EOF, va returna 0. Util, când vrem sa citimtot fis, ierul s, i nu s, tim din start cât de lung este.

Funct,ia este orientata spre text, asta înseamna ca se "uita" la textul citit. Numaru maxim de caractere citite este n-1dar poate sa se opreasca s, i mai repede, daca întâlnes, te sfârs, itul fis, ierului sau, atent,ie, caracterul newline \n.

Funct,ia fgets permite implementarea unei bucle for, care la fiecare pas cites, te câte o linie de text.

3.7. Intrari/ies, iri 47

Page 54: Programarea Calculatoarelor - utcluj.rousers.utcluj.ro/~visoft/teach/programareacalculatoarelor.pdfProgramarea Calculatoarelor, Versiune 0.32 1.2Desfas˘, urare 1.2.1Abstractizare

Programarea Calculatoarelor, Versiune 0.32

Important: Funct,ia fgets este o metoda sigura de a citi de la intrare. Într-un mediu de product,ie, aceasta funct,ienu permite atacuri de genul buffer overrun deoarece numarul de caractere citite este limitat la n. Daca vrem sa citimde la tastatura cu aceasta funct,ie, putem trimite handler-ul standard stdin.

De ret,inut ca, în anumite forme, s, i lucrul cu "internetul" (sockets) poate fi vazut ca o scriere s, i citire din anumitebuffere ale sistemului de operare.

Funct,ia complementara este:

int fputs ( const char * str, FILE * f)

Nimic spectaculos, dar atent,ie, funct,ia este orientata pe s, ir de caractere! Asta înseamna ca va scrie pâna când întâlnes, tevaloarea 0 în zona de memorie str. Va returna o valoare diferita de zero în caz de succes.

3.7.4 Accesul la nivel binar

Aceste funct,ii (foarte similare cu fgets s, i fputs) nu t,in însa seama de cont,inut. Trateaza totul opac.

int fread ( void * ptr, int size, int count, FILE * f)

Se citesc din fis, ierul f, size * count valori s, i se stocheaza în zona de memorie ptr. Separarea acestor doiparametri, în size s, i count este utila pentru ca, de obicei, acest mod de a scrie/citi, în blocuri, este pentru datestructurate (tabele de valori simple sau tabele de structuri). La size va merge de obicei sizeof(tip) iar lacount numarul de elemente. ATENT, IE, ptr trebuie alocat s, i sa aiba suficienta memorie pentru a stoca câte valoritrebuie citite!

Funct,ia fread va returna numarul de elemente citite cu succes.

Funct,ia complementara este:

int fwrite ( const void * ptr, int size, int count, FILE * f )

Similar ca s, i fread. Returneaza numarul de elemente scrise cu succes.

3.7.5 Redirectarea I/O standard

În subcapitolul anterior am spus despre stdin ca este intrarea standard, nu am spus ca este intrarea de la tastatura.

Deoarece sistemul de operare mijloces, te orice comunicare cu exteriorul, acesta permite capturarea acestor intrari s, iies, iri s, i redirectarea lor spre alte utilizari.

Cea mai frecventa folosire (s, i aici utilizatorul programului decide s, i nu creatorul programului) este de a redirectaies, irea standard a unui program spre intrarea altuia.

Metoda aceasta de comunicare între programe este foarte des folosita în linux. Daca programul vostru poate scrie s, iciti date de la intrarile/ies, irile standard, putet,i foarte us, or sa îi adaugat,i facilitatea de a fi înlant,uit prin intermediulbufferelor de intrare s, i ies, ire standard.

De obicei este "nepoliticos" în a nu respecta stdin s, i stdout s, i a fort,a, de exemplu, ies, irea în consola. Daca user-ulvrea sa inhibe output-ul la programul vostru, trebuie sa poata sa o faca simplu, redirectând ies, irea spre o zona faraefect (/dev/null pe linux).

48 Chapter 3. Iterat, ia 2

Page 55: Programarea Calculatoarelor - utcluj.rousers.utcluj.ro/~visoft/teach/programareacalculatoarelor.pdfProgramarea Calculatoarelor, Versiune 0.32 1.2Desfas˘, urare 1.2.1Abstractizare

CHAPTER 4

Probleme propuse

4.1 Introducere

Abordam aici câteva "probleme" care se întâlnesc relativ des în programare. Intrarea în aceste programe se face dinfis, iere deoarece introducerea de la tastatura a unui input complex este mare consumatoare de timp.

4.2 Citire de text din fis, ier

Avem un fis, ier care cont,ine text, pe linii. Vreau sa citesc în memorie acest text.

• Cum citesc un text dintr-un fis, ier?

• Cum citesc o linie de text?

• Cum citesc toate liniile?

• Cum stochez linii de text?

4.3 Parsare text dintr-un s, ir

Avem un fis, ier care cont,ine informat,ii structurate, în format text. De exemplu, date despre un student.

• Cum citesc o linie de text? Vezi problema anterioara.

• Cum "despart" în bucat,i o linie de text?

– Parsare manuala

– sscanf

– strtok -> în iterat,ia III

• Cum pastrez s, i manipulez datele citite? Structuri s, i tabele de structuri

49

Page 56: Programarea Calculatoarelor - utcluj.rousers.utcluj.ro/~visoft/teach/programareacalculatoarelor.pdfProgramarea Calculatoarelor, Versiune 0.32 1.2Desfas˘, urare 1.2.1Abstractizare

Programarea Calculatoarelor, Versiune 0.32

• Citire, stocare s, i afis, are folosind funct,ii

4.4 Liste de vectori

Avem în fis, ier, liste de numere, matrici, structuri numerice mai complexe. Cum le manipulam?

• Citit numere din fis, ier

• Parsarea unui numar nedeterminat de numere dintr-o linie.

– Daca s, tim câte elemente sunt

– În doi pas, i

– Într-un singur pas.

• Pastrat vectori, matrici, liste de vectori.

4.5 Operat, ii cu liste de s, iruri de cuvinte

Procesari de text mai avansate

• Citim linii din fis, ier

• Despart,im liniile în cuvinte

• Stocam cuvintele întâlnite

• Afis, am lungimea fiecarui cuvânt

4.6 Tabele dinamince

Tabelul are lungime fixa în C, cunoscuta la compilare. Alternativa mai flexibila este lucrul cu pointeri. S, i aici însa,necesarul de memorie trebuie cunoscut înainte de a aloca memoria. Ce facem când nu s, tim dinainte, câta memorie netrebuie?

O solut,ie este sa alocam din start câteva elemente (fie 100), apoi, când adaugam al 99-lea element, sa facemurmatoarele operat,ii

• Alocam dublul memoriei anterioare

• Mutam elementele existente în noua zona de memorie

• Dealocam memoria veche

Codul trebuie scris în asa maniera în care operat,iile de adaugare s, i indexare sa fie transparente.

Câteva recomandari de implementare:

• Detaliile despre tabel (adresa zona de memorie, numarul de elemente alocate, ultimul element scris, etc) sa fiepastrate într-o structura

• Alocarea unui astfel de tabel sa se faca de catre o funct,ie

• Adaugarea de elemente sa se faca folosind o funct,ie

• Redimensionarea tabelului sa fie alta funct,ie

• Accesul cu elementele existente sa se faca direct prin membrii structurii.

50 Chapter 4. Probleme propuse

Page 57: Programarea Calculatoarelor - utcluj.rousers.utcluj.ro/~visoft/teach/programareacalculatoarelor.pdfProgramarea Calculatoarelor, Versiune 0.32 1.2Desfas˘, urare 1.2.1Abstractizare

CHAPTER 5

Iterat, ia 3

5.1 Introducere

În aceasta iterat,ie atingem superficial câteva concepte mai avansate de C s, i programarea calculatoarelor, în general.

• Intram un pic s, i în detalii despre cum funct,ioneaza un toochain de compilare s, i câteva implicat,ii ale lui înorganizarea codului.

• Va arat câteva solut,ii la unele situat,ii care apar des, mai ales în manipularea de date cu structura mai complexa.

• În matematica, daca avem o ecuat,ie bine definita, aceasta admite solut,ii bine definite. La fel s, i în programare,sunt probleme bine definite la care solut,ia este s, i ea, bine definita. Trebuie doar sa recunoas, tet,i tipul de problemas, i sa aplicat,i solut,ia.

• Design Patterns. Din cauza ca, codul este mai mult citit decât scris, cel/cea care va citi codul va recunoas, tesolut,ia s, i va s, ti automat cum sa foloseasca codul vostru. Astfel de "perechi" problema - solut,ie, comun întâlniteîn programare se numesc design patterns (Modele sau s, abloane de proiectare). Exista o carte celebra în caresunt descrise cele de baza. Nu intram în detalii acum. Este important sa s, tit,i de existent,a lor s, i ca este rau sareinventat,i roata.

5.2 Toolchain-ul de compilare

La început am facut precizarea ca operat,ia de compilare este de fapt mai complicata. Acum intram în detalii.

Orice limbaj de programare cât de cât folosibil are mai multe instrumente care proceseaza codul sursa pentru a îltransforma în cod mas, ina executabil de un anumit tip de procesor/arhitectura/OS.

De obicei sunt trei astfel de instrumente:

• Preprocesarea

• Compilarea

• Link-editarea.

51

Page 58: Programarea Calculatoarelor - utcluj.rousers.utcluj.ro/~visoft/teach/programareacalculatoarelor.pdfProgramarea Calculatoarelor, Versiune 0.32 1.2Desfas˘, urare 1.2.1Abstractizare

Programarea Calculatoarelor, Versiune 0.32

Fiecare are rolul s, i evident sursa lui de bug-uri.

5.2.1 Preprocesarea

Directiva include

Mai t,inet,i minte prima linie din Hello World?

#include <stdio.h>

Instruct,iunile pentru preprocesor se numesc directive. Ele se recunosc us, or, încep cu caracterul diez # urmat de ocomanda s, i apoi alt,i parametri.

Cea mai folosita instruct,iune este include. Dupa nume, ea include fis, ierul specificat ca parametru. Copy-paste înfis, ierul nostru.

Convent,ia spune ca, daca numele fis, ierului este inclus în paranteze unghiulare < > el va fi cautat în caile standard aletoolchain-ului. Daca numele fis, ierului este cu ghilimele " " atunci va fi cautat în directorii proiectului.

Regulile, mai ales când se cauta în directorii toolchain-ului, sunt complicate. Le vet,i cauta s, i citi când vet,i ajunge salucrat,i în proiecte mari.

Odata ce fis, ierul este gasit, el se pune la pozit,ie, în fis, ierul nostru.

Care e rolul unui asemenea mecanism? El permite sa includem definit,ii s, i prototipuri de funct,ii. În stdio.h avemprototipurile funct,iilor printf, scanf, fopen, etc etc. Atent,ie doar prototipurile funct,iilor! Recitit,i Section 3.6ca sa va aducet,i aminte.

Directivele include se scriu de obicei la începutul fis, ierului sursa.

Directiva define

Aceasta directiva este o substitut,ie de text:

#define CONSTANTA_HAMILTON 0.44

Peste tot în cod unde vom folosi construct,ia CONSTANTA_HAMILTON preprocesorul o va înlocui cu 0.44. Expresii,apel de funct,ii, etc. Except,ie, în constantele de tip s, ir de caractere!

Mai exista #undef care dezactiveza substitut,ia.

Constanta definita anterior se numes, te constanta simbolica.

Aceasta directiva este foarte folosita în a declara constante care sunt valabile în tot codul sursa. Poate avem anumitevalori fizice de care trebuie sa t,inem seama sau parametri de configurare, etc.

Combinate cu directive ca ifdef pot face compilari condit,ionate ale codului. Vedem imediat.

O forma mai complexa a lui define este aceea în care putem specifica parametri. Astfel de construct,ie se numes, teun macrou (a macro, în engleza)

#define nume(p1, p2, ... pn) text_macrou

unde p1 pâna la pn sunt parametri. Parametrii "efectivi" vor fi substituit,i în text_macrou apoi totul va fi pus înlocul din cod unde a fost apelat acest macrou.

Daca textul este prea lung, poate fi împart,it pe mai multe linii, folosind \.

Mare atent,ie, aceste comenzi merg la preprocesor s, i NU la compilator. Daca compilatorul permite o flexibilitate marela spat,iile albe (putem avea 1 sau 1000 de spat,ii albe între variabile s, i operatori) la preprocesor exista reguli mai stricte.

52 Chapter 5. Iterat, ia 3

Page 59: Programarea Calculatoarelor - utcluj.rousers.utcluj.ro/~visoft/teach/programareacalculatoarelor.pdfProgramarea Calculatoarelor, Versiune 0.32 1.2Desfas˘, urare 1.2.1Abstractizare

Programarea Calculatoarelor, Versiune 0.32

Între numele macroului s, i paranteza deschisa NU trebuie sa existe spat,ii albe! Daca nu exista nici un parametru formal,trebuie sa lipseasca s, i parantezele. Parametrii formali se separa cu virgula.

MARE ATENT, IE!!! Exista diferent,e fundamentale între un apel de funct,ie s, i un macrou! La funct,ie, expresiile dinpozit,iile parametrilor reali sunt evaluate, exista probabil conversie de tip, apoi, valoarea rezultata este "trimisa" lafunct,ie. Aici, nu se întâmpla as, a ceva. Exista doar substitut,ii de text.

Exemplu:

Listing 5.1: Definitie de macrou cu erori

#include <stdio.h>#define INMULTIRE(a, b) a * bint inmultire(int a, int b){

return a * b;}int main(){

int i, j;

i = INMULTIRE(2 + 2, 4 + 4);j = inmultire(2 + 2, 4 + 4);printf("macro: %d, functie %d\n", i, j);return 0;

}

În codul de mai sus vreau sa execut (2+2) * (4 + 4). Matematic, rezultatul este 4 * 8 == 32. Simplu? Rulând, primulrezultat este gres, it. Pentru ca, la substitut,ia de text, avem as, a:

#define INMULTIRE(a, b) a * bi = INMULTIRE(2 + 2, 4 + 4);a va fi înlocuit cu 2 + 2, b cu 4 + 4Rezulta 2 + 2 * 4 + 4Acest text va fi inserat inapoi in instructiunei = 2 + 2 * 4 + 4;Respectand prioritatile, rezulta i = 2 + 8 + 4, adica 14.

Pentru a nu avea probleme, punet,i paranteze în textul macroului.

#define INMULTIRE(a, b) (a) * (b)

Observat,i ca nu am ; la sfârs, it.

Alta situat,ie posibil capcana este la instruct,iunile simple s, i compuse. Daca în macrou avet,i doua sau mai multeinstruct,iuni, s, i folosit,i macroul ca o instruct,iune simpla dupa un if sau for, iaras, i apar probleme.

Ce se obis, nuies, te sa se faca este sa se includa macroul în instruct,iunea do{ ... }while(0) iar la ... va scriet,iinstruct,iunile. Construct,ia se gases, te în programele mai complexe (gen kernelul de linux).

Aceste capcane subtile fac ca macrourile sa nu fie chiar primul instrument folosit într-un program.

Exista anumite instrumente din toolchain care pot "injecta" definit,ii în codul sursra. Util, atunci când vrem samemoram în program data compilarii sau versiunea de cod sursa, FARA sa modificam codul sursa. Din nou, ofacilitate folosita în general în proiectele complexe.

5.2. Toolchain-ul de compilare 53

Page 60: Programarea Calculatoarelor - utcluj.rousers.utcluj.ro/~visoft/teach/programareacalculatoarelor.pdfProgramarea Calculatoarelor, Versiune 0.32 1.2Desfas˘, urare 1.2.1Abstractizare

Programarea Calculatoarelor, Versiune 0.32

Compilarea condit, ionata

Cu perechile de directive #ifdef #else #endif se poate selecta ce bucat,i din codul sursa merg mai departe lacompilare s, i care nu. Directiva #ifdef expresie ... #endif va include codul sursa dintre cele doua linii,daca expresie este evaluata la o constanta simbolica definita anterior. Daca nu exista definit,ia, codul se va elimina.

Exista s, i formele:

• #if CONSTANTA care evalueaza la True daca CONSTANTA este diferita de zero (la preprocesare, NU lacompilare).

• ifdef CONSTANTA care preia codul cand constanta ESTE definita.

Exista doua situat,ii frecvent întâlnite:

Includerea condit, ionala a fis, ierelor cu definit, ii de prototipuri.

În general, compilatorului nu îi place sa vada definit un lucru de mai multe ori. As, a cum at,i inclus stdio.h înprogramul vostru, alte biblioteci pot sa includa la rândul lor acest fis, ier. Incluzând s, i acele biblioteci în cod, se poatesa avet,i codul din stdio.h inclus de 4-5 ori. Din aceasta cauza, TOATE fis, ierele care sunt menite a fi inclusetrebuie înconjurate de directive care sa evite includerea duplicata. Construct,ia este simpla. În interiorul fis, ierului ce seintent,ioneaza a fi inclus se pune:

#ifndef __FISIERUL_MEU_H#define __FISIERUL_MEU_H

// Toate declaratiile, prototipurile, etc.

#endif// Ultima linie din fisier.

Compilarea condit, ionata în funct, ie de valorile constantelor simbolice

Exemplul "didactic" este când vrem sa afis, am valori intermediare ale execut,iei programului dar nu vrem ca acesteinstruct,iuni de afis, are sa ajunga în codul clientului.

Definim o constanta simbolica iar codul de debug il înconjuram de instruct,iuni de compilare condit,ionala.

1 #include <stdio.h>2 #define SHOW_DEBUG 13 int main()4 {5 printf("Production code\n");6 #if SHOW_DEBUG7 printf("Am ajuns aici cu codul\n");8 #endif9 printf("Production code\n");

10 return 0;11 }

Putem dezactiva toate liniile de depanare (linia 7) setând constanta SHOW_DEBUG la 0.

În practica, putem profita de facilitatea ca toolchain-ul poate injecta definit,ii de constante simbolice s, i putem definisect,iuni de cod care vor fi compilate (sau nu) în funct,ie de setarile toolchain-ului.

Foarte util când avem cod dependent de sistemul de operare sau arhitectura pentru care scriem cod. Mai precis, cândvrem sa scriem cod pentru mai multe arhitecturi/sisteme de operare. Majoritatea covârs, itoare a funct,iilor si expresiilordin C sunt portabile, adica detaliile de implementare sunt ascunse în biblioteci precompilate ce vin cu toolchain-ulspecific, dar unele detalii ajung pâna la nivelul codului sursa. De exemplu, calea unui fis, ier. Formatul de cale estediferit în Windows fat, a de Linux. Lucrul cu firele de execut,ie (nu le-am aminiti aici) este iaras, i dependent de sistemulde operare.

54 Chapter 5. Iterat, ia 3

Page 61: Programarea Calculatoarelor - utcluj.rousers.utcluj.ro/~visoft/teach/programareacalculatoarelor.pdfProgramarea Calculatoarelor, Versiune 0.32 1.2Desfas˘, urare 1.2.1Abstractizare

Programarea Calculatoarelor, Versiune 0.32

Daca vrem sa facem un cod care sa ruleze pe mai multe arhitecturi, bucat,ile de cod dependente de OS vor trebui izolates, i înconjurate de astfel de directive de preprocesare.

Utilizatorul va seta toolchain-ul sa produca cod pentru o anumita combinat,ie de arhitectura / OS iar toolchain-ul vainjecta aceste specificat,ii în fiecare fis, ier ce urmeaza a fi compilat.

Detaliile sunt multe, programele care configureaza toolchain-urile au documentat,iile lor s, i "limbajele" lor specifice.Mecanismul prezentat mai sus este însa comun mai multor astfel de instrumente.

În funct,ie de locul/firma unde vet,i lucra, le vet,i învât,a încet încet detaliile.

Ca idee, exista instrumentul cmake care genereaza script-uri de execut,ie pentru instrumentul make. Instrumentulmake va executa respectivele script-uri, apelând preprocesorul, compilatorul s, i apoi link-editorul specific configurarii.

Este relativ comun întâlnita situat,ia în care compilam un cod targetat pentru o alta arhitectura decât cea pe care seexecuta compilarea. Exemplu clasic este compilarea codului ce va rula pe un AVR ("Arduino"), compilare efectuatape o mas, ina x86.

Generatoare de cod

Exista structuri de cod complexe s, i repetitive, a caror generare este bine a fi automatizata. De exemplu, vrem saprecalculam toate valorile lui sin(x) s, i sa le scriem într-un fis, ier, fis, ier care va fi inclus s, i folosit în codul nostru.

Un astfel de program, care genereaza cod, va trebui apelat înainte de preprocesor. Legarea de toolchain se va face totprin setari s, i configurari. Nu le vet,i porni manual ci vor fi pornite automat de script-urile de compilare.

Exemplul dat este relativ trivial. De obicei, programul de generare are un limbaj specific s, i fis, iere de intrare scrise înrespectivul limbaj. Cautat,i lexers and parsers, sau protocoale de comunicare (protobuffers, JSON, etc). Un protocolde comunicare (de exemplu protobuffers de la Google) are "compilatoare" care transforma fis, ierul cu specificat,iileprotocolului din formatul specific (ex .proto) în C, Java, Python sau alt limbaj dorit. Codul astfel generat estecapabil sa manipuleze informat,ia transmisa/primita conform specificat,iilor voastre.

Un alt use case al generatoarelor de cod este atunci când înlant,uim mai multe instrumente pentru a obt,ine rezultatuldorit:

Acest curs este scris într-un limbaj de formatare a textului, care, este procesat de un framework scris în python(sphinx) care genereaza alt limbaj, numit LaTeX, care apoi este procesat de un alt toolchain, care toolchaingenereaza documentul pdf. Complicat? Probabil setarile init,iale, pentru ca, în timpul lucrului, fiecare nivel introduceabstractizari, astfel încât, la cel mai înalt nivel utilizatorul se poate concentra la ceea ce este important s, i nu la detaliice se pot automatiza. Cu orice instrument, exista avantaje s, i dezavantaje (tradeoffs). Exemplele de cod de aici sunt"colorate" automat dar este mai dificil sa personalizezi o anumita sect,iune.

5.3 Programarea modulara

Preprocesarea s, i configurarea toolchain-ului, prototiparea funct,iilor, evitarea dublei declarat,ii, toate sunt instrumentecare ne ajuta sa organizam codul.

Un program complex poate fi împart,it pe diverse tipuri de funct,ionalitat,i. Sa luam exemplu biblioteca standard. Avemfunct,ii matematice, avem intrari ies, iri, lucrul cu s, iruri de caractere, etc. Are sens sa grupez împreuna funct,iile deprocesare s, iruri de caractere pentru ca ele de obicei sunt folosite împreuna. La fel cu cele matematice.

Criteriile de grupare a codului, CE anume grupez, este o arta pe care o sa o câs, tigat,i în timp. Sunt câteva linii directoare

• Codul care are funct,ionalitate înrudita se grupeaza în acelas, i modul (eg funct,ii matematice)

• Funct,iile care sunt dependente unele de altele (sau se folosec împreuna) de obicei se grupeaza în acelas, i modul(de ex. fread depinde de fopen prin intermediul structurii FILE chiar daca fread nu apeleaza niciodatadirect, fopen)

5.3. Programarea modulara 55

Page 62: Programarea Calculatoarelor - utcluj.rousers.utcluj.ro/~visoft/teach/programareacalculatoarelor.pdfProgramarea Calculatoarelor, Versiune 0.32 1.2Desfas˘, urare 1.2.1Abstractizare

Programarea Calculatoarelor, Versiune 0.32

• Exista un tradeoff între a avea foarte multe module s, i a avea un singur modul pentru tot proiectul.

• Daca folosit,i des o bucata de cod s, i o inserat,i cu copy/paste este momentul sa realizat,i un modul pentru codulrespectiv. Codul duplicat este un lucru urât s, i aducator de bug-uri greu de depistat.

Modulele se pot organiza s, i ele în pachete. Modul în care se realizeaza asta depinde mult de platforma, de limbaj, etc.

Din fericire pentru un proiect, împart,irea pe module nu trebuie sa fie hotarâta din momentul crearii programului. Existaconceptul de refactoring (rescriere) în care, pastrând funct,ionalitatea, se modifica codul care implementeaza respectivafunct,ionalitate.1

În concluzie, deocamdata nu va batet,i capul prea tare cu programarea modulara (trebuie sa s, tit,i not,iunile pentruexamen!). Odata ajuns, i sa lucrat,i la proiecte complexe vet,i prinde subtilitat,ile. Profitat,i s, i învat,at,i cum fac alt,ii. Darmare atent,ie sa nu învat,at,i deprinderi gres, ite. De obicei o împart,ire buna pe module, duce la un cod us, or de scris s, i deîntret,inut.

Sfatul meu este sa va documentat,i temeinic instruct,iunile s, i modul de configurare a toolchain-ului. Din experient, a,sunt lucruri deloc intuitive care se uita repede. Nu vret,i sa re-citit,i n pagini de StackOverflow pentru ceva minor.

Acum, ne concentram pe CUM. Cum facem programare modulara în C?

Am vazut în sect,iunea anterioara directiva #include. S, i am precizat ca de obicei acolo gasim prototipuri de funct,iiiar codul care implementeaza aceste funct,ii se gases, te în alta parte.

Înainte de a explica legatura dintre fis, ierele .h s, i .c mai dam câteva detalii despre toolchain pentru a înt,elege maibine restrict,iile s, i modul de declarare a modulelor.

5.3.1 Compilarea s, i Link-editarea

Un modul în C este un fis, ier .c, compilabil, care genereaza un cod intermediar, neexecutabil direct, numit cod obiect.

De crearea codului obiect se ocupa compilatorul, DUPA ce a primit fis, ierele sursa de la preprocesor. În codul obiectgasim toate instruct,iunile cod mas, ina corespunzatoare instruct,iunilor din codul nostru sursa. O except,ie notabila suntapelurile la funct,ii. În loc de instruct,iunea de salt la bucata de cod a funct,iei, se va pune un identificator special, creatpe baza numelui funct,iei, precum s, i cod pentru punerea parametrilor reali pe stiva.

Compilarea (generarea de cod obiect) se face independent pe fiecare fis, ier în parte. De aceea, specificarea prototipuluifunct,iei, este singura metoda prin care compilatorul "s, tie" ca exista respectiva funct,ie undeva în cod, s, i s, tie cum sagestioneze parametrii de intrare s, i ies, ire.

Codul obiect merge la link-editor. Acesta preia TOATE fis, ierele obiect din proiect, cauta declarat,iile de funct,ii s, ipotrives, te fiecare apel de funct,ie scris de compilator cu declarat,iile corespunzatoare. Astfel, link-editorul "leaga" sauînlocuies, te identificatorul scris de compilator cu adresa reala a codului ce trebuie apelat. Apoi, link-editorul creaza unfis, ier executabil pentru arhitectura/OS-ul pentru care a fost configurat.

Link-editorul lucreaza cu TOATE fis, ierele obiect generate la pasul anterior. Daca o funct,ie are prototip dar NU arecorp, link-editorul va semnaliza eroarea.

Aici este o nuant, a, unele funct,ii pot fi deja compilate (ex biblioteca standard). Codul mas, ina corespunzator poate existaîn fis, iere din sistemul de operare (C:\windows\system32\) sau în biblioteci din toolchain. Daca link-editorul iaacest cod s, i îl copiaza la noi în executabil, efectuam o link-are statica. Daca se trece doar un apel special, (apel ce vafi interceptat s, i rezolvat de sistemul de operare, la execut,ie) efectuam o link-are dinamica.

Când trebuie sa facem una s, i când facem alta? Un pic complicat. În principiu daca lucrat,i doar pe mas, ina voastra, vet,iface link-are dinamnica.

Daca trimitet,i executabilul la client (sau pe alta mas, ina) e indicat sa facet,i link-are statica.

1 Pastrarea funct,ionalitat,ii codului se poate garanta cu ajutorul testarii automate. Nu vom putea intra la limbajul C în aceste concepte. Trebuiesa s, tit,i ca exista instrumente (s, i metodologii) de testare cam pentru orice limbaj. Când va vor trebui, le cautat,i s, i aplicat,i.

56 Chapter 5. Iterat, ia 3

Page 63: Programarea Calculatoarelor - utcluj.rousers.utcluj.ro/~visoft/teach/programareacalculatoarelor.pdfProgramarea Calculatoarelor, Versiune 0.32 1.2Desfas˘, urare 1.2.1Abstractizare

Programarea Calculatoarelor, Versiune 0.32

Evident, orice regula are s, i except,ii. Cautat,i packaging. Depide de instrument, de OS-ul targetat, de specificat,iileclientului etc.

Important: Compilatorul, ca si componenta a toolchain-ului "traduce" instruct,iunile din cod C în cod mas, ina.Declarat,iile de funct,ii le adnoteaza cu un nume special. Când exista un apel de funct,ie, adresa la care va trebui sa saraprocesorul este înlocuita de o adnotare. Din cauza ca la compilare, compilatorul are acces la prototipul funct,iei, celedoua adnotari vor fi identice.

Link-editorul va fi cel care va înlocui adnotarea de apel cu adresa efectiva.

Aici se deschide o întreaga poveste despre convent,iile de apel s, i de modurile de generare a adnotarii, discut,ie pe carenu va invit sa o studiat,i decât în momentul care vet,i genera biblioteci.

5.3.2 Modulele

Acum sa revenim. Un modul în C, este un fis, ier .c care va genera un fis, ier cod obiect. Fiecare modul este compilatîn mod independent de celelalte (atent,ie e vorba de compilatorul ca parte componenta din toolchain)

Pentru ca operat,ia de compilare sa decurga bine, TOATE variabilele, constantele s, i funct,iile trebuie DECLARATEînainte de prima utilizare.

Aceasta declarare se face DE OBICEI incluzând fis, ierele .h care cont,in prototipurile de funct,ii a caror implementareeste în alte module dar care sunt apelate din modulul curent.

Atunci când folosim funct, ii din alte module:

Includem prototipul funct,iilor în codul nostru. Putem sa includem strict prototipul. Nu va recomand, pentru claritate.

Atunci când scriem un modul, ce va fi folosit de alt, ii:

Cream doua fis, iere, unul .h s, i unul .c. Vom scrie în fis, ierul .h numit header toate prototipurile funct,iilor care vremsa le facem disponibile altor module. "Pazim" fis, ierul header împotriva incluziunilor multiple folosind directive depreprocesare.

Fis, ierul nostru .c va cont,ine declarat,ia s, i corpul funct,iilor, ca s, i pâna acum. Neaparat sa includet,i fis, ierul header creatanterior!

Din cauza ca v-am dat detalii despre cum funct,ioneaza toolchain-ul, "ret,eta" de creare a modulelor din aceasta sect,iuneeste scurta s, i nu necesita explicat,ii multe.

Mai mult, înt,elegerea (chiar si daca superficiala) a modului de funct,ionare a toolchain-ului va ajuta sa rezolvat,i bug-urile ce vin odata cu programarea modulara.

Ca orice lucru iterativ, e mai simplu sa scriet,i codul doar în fis, ierul .c s, i pe masura ce interfat,a cu alte module sestabilizeaza, pe masura ce implementat,i si rafinat,i codul din modul, luat,i prototipurile funct,iilor s, i creat,i / populat,ifis, ierul header.

Modul în care se executa compilarea s, i link-editarea are impact asupra modului cum manipulam fis, ierele. IDE-ultrebuie sa fie informat de faptul ca anumite fis, iere .C vor face parte din acelas, i proiect s, i ca trebuie trimise împreunala link-editor.

5.3. Programarea modulara 57

Page 64: Programarea Calculatoarelor - utcluj.rousers.utcluj.ro/~visoft/teach/programareacalculatoarelor.pdfProgramarea Calculatoarelor, Versiune 0.32 1.2Desfas˘, urare 1.2.1Abstractizare

Programarea Calculatoarelor, Versiune 0.32

5.4 Variabile statice s, i constante

5.4.1 Modificatorul const

Uneori trebuie sa declaram valori care nu se vor modifica de-a lungul execut,iei programului. O alternativa este directiva#define. Alta alternativa este sa declaram variabile constante:

const int pi = 3.1415;

Orice atribuire a lui pi va genera eroare. Variabilele declarate const nu sunt lvalue. Deasemenea e posibil ca,compilatorul, sa efectueze anumite optimizari. De exemplu, unele compilatoare aloca variabilele constante în zona decod, zona protejata la scriere de sistemul de operare.

Dupa cum va atrageam atent,ia la început, în practica, codul este mai mult citit decât scris. În decursul evolut,ieiproiectului, se poate întâmpla ca anumite bucat,i de cod sa se modifice. Unele funct,ii trebuie sa nu modifice datele deintrare, mai ales când vine vorba de pointeri sau tablouri.

Folosirea lui const în antetul funct,iilor ne asigura ca, modificari viitoare ale codului nu vor modifica cont,inutulmemoriei primite ca parametru. Cumva, atunci când scriem prima data codul s, i s, tim aceasta restrict,ie, pare redundantsa mai scriem const. Atent,ie, acest modificator îl scriet,i ca o asigurare pentru viitor.

Cineva care va vroi sa modifice datele de intrare, va trebui sa s, tearga acel const. Aceasta modificare de obicei trebuie"aprobata" s, i este greu de introdus în proiect, fara o justificare.3 Însa, o modificare accidentala a datelor de intrarepoate trece neobservata.

Exemplu de "protejare" a unei variabile împotriva modificarii accidentale:

int func(const char* sir){sir[4] = 'a'; // Ilegal! Eroare de compilare

}

Din pacate "magia" const dispare atunci când pointerul sir este trimis spre o alta funct,ie care nu are modificatorulconst la parametrii formali.

5.4.2 Modificatorul static

La nivelul unei funct,ii este util câteodata sa pastram valori între mai multe apeluri. Nerecomandabil, dar util.

Putem declara o variabila cu modificatorul static, aceasta fiind alocata în zona statica a memoriei. Ea nu va fiafectata de ceea ce se întâmpla pe stiva.

Atent,ie, variabilele statice la nivel de funct,ie, pot introduce bug-uri subtile. Totus, i, ele îs, i au utilitatea când vret,i safacet,i o funct,ionalitate de tip state machine unde trebuie sa existe o persistent, a între apeluri.

În biblioteca standard avem strtok care funct,ioneaza în acest fel.

MARE ATENT, IE, când vet,i învat,a despre programarea pe fire de execut,ie (nu aici),vet,i afla ca, compilatorul NU alocacâte o variabila statica pentru fiecare fir de execut,ie. Bug-uri FOOAAARTE greu de depistat daca avet,i câteva apeluriconcurente la o funct,ie cu variabile statice, fiecare apel modificând la întâmplare variabila.

Cautat,i conceptul de thread local memory. Daca va trebuie multe astfel de variabile e semn ca at,i organizat gres, itcodul. Aleget,i alt design pattern pentru a rezolva problema SAU folosit,i alte paradigme (ex programarea orientata peobiect sau funct,ionala).

S, i pentru ca nu exista prea multe elemente cu o singura semnificat,ie în C, mai avem un rol al modificatorului static.

3 La "code reviewing" un coleg se uita peste codul scris de voi s, i îl aproba spre a fi "integrat" în proiectul mare. În aceasta etapa, anumitemodificari la codul existent (de exemplu s, tergerea unui modificator const) atrag un semnal de alarma.

58 Chapter 5. Iterat, ia 3

Page 65: Programarea Calculatoarelor - utcluj.rousers.utcluj.ro/~visoft/teach/programareacalculatoarelor.pdfProgramarea Calculatoarelor, Versiune 0.32 1.2Desfas˘, urare 1.2.1Abstractizare

Programarea Calculatoarelor, Versiune 0.32

Daca îl folosim ca modificator la o variabila globala sau la o funct,ie, acestea NU sunt vizibile din exteriorul modulului.Util pentru a încapsula la nivel de modul anumite informat,ii.

5.4.3 Modificatorul extern

Modificatorul extern este folosit pentru a specifica compilatorului ca altcineva (în alta parte) este responsabil dealocarea memoriei pentru variabila declarata cu acest modificator. Adresa ei va fi descoperita la link-editare.

Atent,ie, într-unul din modulele proiectului TREBUIE sa existe declarat,ia variabilei, cu acelas, i nume, declarat,ieglobala, fara modificatorul static.

Daca simt,it,i nevoia sa lucrat,i mult cu astfel de variabile, pentru a comunica între module, ori nu e bine structuratprogramul ori trebuie sa va orientat,i spre alte paradigme (probabil POO)

5.5 Lucrul cu date structurate

În iterat,ia 2 am vazut CUM declaram structuri pentru a pastra grupat date eterogene dar legate din punct de vederelogic s, i cum rezervam zone de memorie de acelas, i tip (tabelul).

Acum vedem nis, te cazuri reale de combinare a acestor instrumente:

• Alocarea s, i lucrul cu matrici 1D, 2D, etc. cu câteva variante pentru lucrul mai facil

• S, iruri de caractere s, i tabele de s, iruri de caractere

• Structuri recursive

• Lucrul cu bit,i

5.5.1 Array-uri de pointeri

Problema: Vrem sa stocam o matrice bidimensionala

Trebuie sa s, tim dimensiunile matricii adica numarul de rânduri m s, i numarul de coloane n. Acesta poate fi calculat întimpul rularii programului pentru ca memoria va fi alocata dinamic în heap.

Ne folosim de faptul ca putem avea tabel de pointeri la pointeri. Tabelul de pointeri, cu m valori va stoca de faptrândurile de date. Fiecare rând de date este un pointer la o zona de memorie unde avem alocate n locat,ii pentru tipulstocat în matrice.

Sa recapitulam. Cum declaram o variabila de tip pointer? double *p. Cum îi rezervam memorie? p =(double*)malloc(sizeof(double) * n). Variabila p va pointa spre o zona de memorie unde avemrezervat loc pentru a lucra cu n valori double.

Daca vrem sa construim un tip ce pointeaza spre un pointer, putem folosi parantezele: (double *)* p. Conformtabelului Tabelul 3.1 s, tim ca operatorul * se asociaza de la dreapta la stânga as, a ca, construct,ia anterioara esteechivalenta cu double **p.

Descriind mai larg variabila p: Variabila p este un pointer spre (acum urmeaza tipul spre care pointeaza) double *.Tipul double * descrie: un pointer spre tipul double.

Grupând totul, obt,inem: p este un pointer spre un pointer spre tipul double. Intuitiv s, i empiric, daca pointerul are o"stelut, a" vorbim de un vector, daca are doua sau mai multe, de matrice 2D sau 3D. Atent,ie, este o observat,ie empirica,NU este o regula!

Am rezolvat cea mai "grea" problema, declararea tipului. Sa vedem cum alocam memoria. Se va face în doi pas, i:

1) Alocarea memoriei pentru vectorul de rânduri (vectorul de pointeri)

5.5. Lucrul cu date structurate 59

Page 66: Programarea Calculatoarelor - utcluj.rousers.utcluj.ro/~visoft/teach/programareacalculatoarelor.pdfProgramarea Calculatoarelor, Versiune 0.32 1.2Desfas˘, urare 1.2.1Abstractizare

Programarea Calculatoarelor, Versiune 0.32

2) Alocarea memoriei pentru fiecare rând.

Listing 5.2: Alocare s, i dealocare matrice 2D de tip double

double** alocare_2d_double(int m, int n){double **matrice = (double**)malloc(sizeof(double*) * m);for(int i = 0; i < m; i++)

matrice[i] = (double*)malloc(sizeof(double) * n);return matrice;

}

void dealocare_2d_double(double ** ptr, int m){for(int i = 0; i < m; i++)

free(ptr[i]);free(ptr);

}

La fel, se pot face funct,ii pentru alocari de matrici 3D, sau de alte tipuri (int, float, etc). Exista mai multeposibilitat,i de a reorganiza codul (ex sa trimit pointerul unde sa se faca alocarile ca adresa, dar mie personal, aceastaforma mi se pare cea mai simpla). Funct,ia de dealocare se poate simplifica un pic, facând-o sa accepte pointeri de tipvoid. Funct,ia free() trebuie sa s, tie ce tip de date se pastreaza acolo, doar adresa. Dar atent,ie, va trebui sa fort,at,iconversia elementului trimis la tipul void folosind operatorul de cast: (void **)

Din pacate limbajul C nu are construct,ii de limbaj ce sa permita folosirea unui tip abstract, tip ce sa se trimita caparametru în momentul scrierii apelului de funct,ie. În C++, Java, exista conceptul de Templates/Generics unde, uneifunct,ii, la apel, putem sa îi specificam s, i tipul pentru anumite variabile. Când scriem prototipul funct,iei îl scriemfolosind un tip abstract (asemanator cu o variabila formala). La apel, acest tip va fi înlocuit cu tipul concret.

Atent,ie, acest mecanism implica mai mult preprocesorul decât compilatorul. Acesta, va "culege" din cod toateapelurile la funct,ie s, i va crea copii pentru funct,ie, folosind fiecare tip concret întâlnit. Nimic magic, dar ne scutes, te deo gramada de munca. Din nou, din pacate, în C, nu exista acest mecanism.

Important: Suntem responsabili de alocarea s, i dealocarea memoriei. Trebuie avut grija sa dealocat,i resursele folosite.

Accesul la elementele matricii este foarte simplu. Daca double **p a fost alocat cu metoda de mai sus, putem scriedirect p[i][j] pentru a accesa elementul de pe rândul cu indexul i s, i coloana cu indexul j. Pret,ul "platit" pentrudificultatea alocarii este câs, tigat în simplitatea folosirii.

5.5.2 Matrice în structura

Daca folosim codul de mai sus, vom constata ca trebuie sa t,inem minte trei valori: adresa de memorie a tabelului 2D s, inumarul de elemente de pe cele doua dimensiuni. Daca avem mai multe matrici, avem o sursa de confuzie si bug-uri.Ar fi bine daca am putea sa le grupam împreuna.

Acest concept, aceasta nevoie de a grupa anumite date împreuna, deoarece ele sunt strâns legate, este un conceptfundamental în programarea orientata pe obiect. Este conceptul de încapsulare. Limbajele care permit aceastaparadigma au mecanisme puternice de grupare a datelor, a datelor împreuna cu codul care manipuleaza datele,mecanisme de "ascundere" s, i protejare a datelor s, i codului, mecanisme de alocare s, i dealocare automata a memoriei,etc.

Acum vom folosi doar tipul de structura pentru a grupa datele necesare unei matrici (nu putem vorbi despre încapsularepentru ca nu putem filtra accesul):

60 Chapter 5. Iterat, ia 3

Page 67: Programarea Calculatoarelor - utcluj.rousers.utcluj.ro/~visoft/teach/programareacalculatoarelor.pdfProgramarea Calculatoarelor, Versiune 0.32 1.2Desfas˘, urare 1.2.1Abstractizare

Programarea Calculatoarelor, Versiune 0.32

Listing 5.3: Gruparea informat,iilor despre matricea bidimensionala,într-o structura

typedef struct{double ** data;int m, n;

} Matrice_double;

Acum putem declara matrici folosind Matrice_double m1, m2;. Exercit,iu: Modificat,i codul din Cod 5.2 pentrua lucra cu structura definita mai sus.

Important: Atent,ie! Când folosim structura de mai sus, se aloca memorie în mai multe zone. Pentru variabilele detip Matrice_double se aloca pe stiva memorie pentru doi întregi s, i o adresa. Pe heap se vor aloca m zone continue,de lungime n * sizeof(double). Plus memorie pentru memorarea celor m adrese spre zonele continue.

De fapt, pentru orice pointer, pattern-ul de bit,i ai adresei spre care pointeaza va trai pe stiva iar memoria rezervata peheap.

Stiva este curat,ata automat de compilator la ies, irea din funct,ie. Dar ATENT, IE MARE! Compilatorul nu sta sa verificefiecare tip s, i sa ia masuri speciale de curat,ire. Este responsabilitatea voastra sa curat,at,i memoria alocata în funct,ie!Daca adresele alocate în funct,ie se pierd, va trebui sa va asigurat,i ca dealocarea are loc înainte de ies, irea din funct,ie.Daca adresele se vor transmite în exterior, tot voi va trebui sa de dealocat,i când at,i terminat de lucrat.

Observat,ii:

1) Am declarat anterior o matrice dreptunghiulara m x n. Metoda de alocare va "împras, tia" fiecare rând prinmemorie. Nu exista nici o garant,ie ca ele vor fi consecutive.

2) Metoda de alocare (câte un rând odata) permite sa avem rânduri de lungimi diferite. Practic nu o sa mai avemobiectul matematic matrice ci o lista de vectori.

Putem returna din funct,ia de alocare o structura ce cont,ine o astfel de matrice (declarata mai sus):

Listing 5.4: Alocarea unei matrici bidimensionale (vezi Cod 5.2 pentrudetalii)

Matrice_double functie_alocare(int m, int n ){Matrice_double m;.....return m

}

.......Matrice_double mat = functie_alocare(5, 10);

Haidet,i sa vedem ce se întâmpla s, i de ce nu avem pierderi de memorie. Ne uitam la listing-urile: Cod 5.2, Cod 5.3 s, iCod 5.4

1) În interiorul unei funct,ii, declar o variabila de tip Matrice_double, aloc memorie cu malloc pentrupointerii data s, i scriu return.

2) Compilatorul ia bit,ii variabilei de tip structura creata în funct,ie s, i (prin intermediul stivei) îi va scrie in variabiladin codul principal. (Datorita expresiei de atribuire)

3) Noua variabila, de tipul Matrice_double va cont,ine pattern-ul de bit,i identic cu variabila (deja distrusa) dininteriorul funct,iei de alocare. Daca calculam cam câta memorie a fost manipulata pe stiva, vedem ca aceastavaloare are cât,iva zeci de octet,i (o adresa de memorie s, i doi întregi). Cantitatea mare de memorie alocata, a

5.5. Lucrul cu date structurate 61

Page 68: Programarea Calculatoarelor - utcluj.rousers.utcluj.ro/~visoft/teach/programareacalculatoarelor.pdfProgramarea Calculatoarelor, Versiune 0.32 1.2Desfas˘, urare 1.2.1Abstractizare

Programarea Calculatoarelor, Versiune 0.32

ramas fixa. Adresa ei nu s-a schimbat. S, i nici nu s-a pierdut. Avem variabila mat care cont,ine un pointer spreacea zona alocata.

Comoditatea folosirii unor astfel de construct,ii depas, es, te cu mult micile penalizari de performant, a.

5.5.3 Matrici continue

Uneori, pentru a câs, tiga viteza, preferam sa avem memoria rezervata continuu. Sa vedem cum obt,inem aceasta alocarecontinua.

Pentru o matrice 2D, de dimensiunea m x n (m linii s, i n coloane) trebuie rezervata m * n * sizeof(tip)octet,i. Pointerul va fi simplu, nu dublu sau triplu. Vom face o singura alocare, nu pentru fiecare element din rând.

Daca vrem sa accesam datele de la indecs, ii i, j, va trebui sa calculam adresa: a = i * n + j. Adica, sarim irânduri de lungime n pentru a ajunge la începutul rândului i apoi mergem la al j-lea element din rând.

Exemplu: tab[i * n + j] = 0.

Alocarea continua are avantajul ca, uneori, lucrul cu matrici este mai rapid ca timp de execut,ie. Operat,ia matematicade convolut,ie, folosita foarte mult în procesarea de imagini lucreaza cu valori apropiate ca indecs, i, cu elementul curent.Daca alocarea este discontinua s-ar putea ca elementul de pe rândul i-1 sa fie foarte departe în memorie.

Costul (ca timp de execut,ie) aducerii unui element din memorie este cam de 100 de ori mai mare decât calculul cuunitatea ALU sau alte operat,ii ce au loc doar în procesor. Mai mult, exista posibilitatea ca, la alocarea continua,elementele de care este nevoie sa existe deja în cahce, aduse automat de un acces anterior la memorie.

Deocamdata nu trebuie sa va batet,i capul cu astfel de optimizari. E suficient sa s, tit,i ca sunt cazuri justificate în careaccesul cu p[i][j] nu este ales implicit ca cea mai buna solut,ie.

5.5.4 S, iruri de caractere

Am vazut cum putem stoca un s, ir de caractere. Acum vedem pe scurt câteva forme de a stoca liste de cuvinte.

În primul rând, ne trebuie o zona de memorie capabila sa stocheze s, iruri de caractere. char* este opt,iunea evidenta.Pentru o lista de cuvinte, avem tabelele.

Combinând init,ializarea tabelelor cu constante de tip s, ir de caractere putem avea construct,ii de genul:

char* lista_nume[] = {"Popescu", "Ion", "Mircea"};

//Operatii:printf("%s\n", lista_nume[2]);

Constantele de tip s, ir de caractere se pastreaza într-o zona speciala de memorie, probabil lânga cod (depinde decompilator). Din cauza ca sunt într-o zona protejata la scriere, acestea se pot doar citi. În contextul codului de maisus, orice încercare de schimbare a unui caracter din nume, va duce la evacuarea programului din memorie. Atent,ie,variabila lista_nume traies, te pe stiva, deci, este perfect legal sa schimb cu totul o adresa de acolo (adica un numedin lista noastra de persoane). Asta înseamna schimbarea adresei de memorie spre care pointeaza locat,ia de pe stiva. :

lista_nume[0][2] = 'b' // Ilegal!lista_nume[0] = (char*)malloc(sizeof(char)*1000) // Legal!

Evident, daca pointerul din lista de s, iruri pointeaza spre o zona non constanta, nu mai e o problema schimbareacont,inutului.

62 Chapter 5. Iterat, ia 3

Page 69: Programarea Calculatoarelor - utcluj.rousers.utcluj.ro/~visoft/teach/programareacalculatoarelor.pdfProgramarea Calculatoarelor, Versiune 0.32 1.2Desfas˘, urare 1.2.1Abstractizare

Programarea Calculatoarelor, Versiune 0.32

Buffer overrun

Vret,i sa citit,i un nume de persoana s, i îl afis, at,i. Construct,ia:

char sir[100];scanf("%s", sir);

este deosebit de periculoasa, deoarece, daca de la intrare se citesc mai mult de 100 de caractere, se suprascrie stiva.

Facet,i un exercit,iu. Scriet,i bucata de cod de mai sus, într-o funct,ie. Alocat,i mai put,ine elemente pentru sir (ex 5valori). Afis, at,i ce at,i citit. Apelat,i funct,ia din main().

Introducet,i 3-4 caractere legate (fara spat,ii). Observat,i ca totul funct,ioneaza cum trebuie.

Introducet,i 10-15 caractere, fara spat,ii. Observat,i cum programul este evacuat din memorie, cu o eroare. Din pacate,o structurare cu atent,ie a datelor de intrare permite unui atacator sa preia controlul calculatorului.

Esent,a este ca

1) scanf NU S, TIE CÂTA memorie are la dispozit,ie. Codul din scanf va citi pâna când va întâlni un caracter albsau newline sau sfârs, it de fis, ier.

2) Stiva "cres, te" de sus în jos. Variabila sir este alocata pe stiva, deasupra ei fiind adresa de întoarcere dinfunct,ia curenta.

3) scanf va scrie de la adresa de început a variabilei str, peste adresa de întoarcere din funct,ia curenta. Aici sescrie codul malit,ios care preia controlul calculatorului

4) Procesorul lasa pe stiva adresa de întoarcere din funct,ia curenta s, i se as, teapta sa preia aceasta informat,ie, de peaceeas, i pozit,ie de pe stiva.

De obicei, acest scanf cites, te date de la ret,ea sau de la alte dispozitive, nu de la tastatura. Multe, foarte multe atacurise bazeaza pe acest tip de citire, lasate prin codul sistemelor de operare, driverelor sau a unor utilitare.

5.5. Lucrul cu date structurate 63

Page 70: Programarea Calculatoarelor - utcluj.rousers.utcluj.ro/~visoft/teach/programareacalculatoarelor.pdfProgramarea Calculatoarelor, Versiune 0.32 1.2Desfas˘, urare 1.2.1Abstractizare

Programarea Calculatoarelor, Versiune 0.32

Listing 5.5: Exemplu de cod susceptibil la un atac

void func(){char str[5];scanf("%s", str);

}main(){

func()printf("Gata");

}

În figura de mai jos se vede stiva în momentul în care execut,ia este în interiorul codului din scanf ( Cod 5.5.):

Stiva program

Adresa de întoarcere din func

Variabila str

Adresa de întoarcere din scanf

Variabile locale scanf

stivacres

, testrse

populeaza

Se observa direct,ia în care scanf va popula zona de memorie str. Citirea corecta se face cu funct,ia fgets careprimes, te ca s, i parametru, dimensiunea maxima pe care o poate citi (care este alocata).

5.5.5 Structuri recursive

Pointerii ocupa acelas, i numar de octet,i, indiferent de tipul spre care pointeaza. Acest lucru permite definirea destructuri de date recursive.

De ce mi-ar trebui asa ceva? Sa luam un exemplu. Avem un tabel de N elemente s, i trebuie sa inseram un elementpe pozit,ia 2. Ce facem? Mutam toate elementele de la pozit,ia 2 pâna la N, pe pozit,iile 3 la N+1. Bineînt,eles începândde la ultima pozit,ie. Câte operat,ii facem? Aproximativ N operat,ii. Daca pentru a rezolva problema clientului trebuiesa facem foarte des aceasta operat,ie, codul va rula încet, s, i va încetini odata cu cres, terea numarului de elemente.

Lista este o structura de date fundamentala care permite efectuarea rapida a operat,iilor de inserare. Vet,i învat,a maimulte la alte materii, aici va vorbesc doar de mecanica declararii structurii.

La fiecare element al listei, avem o legatura spre urmatorul element. Când vrem sa o parcurgem, începem cu primulapoi ne mutam pe urmatorul (s, tim care este, pentru ca fiecare element pastreaza legatura spre urmatorul). Când vremsa inseram, cream un nou element dupa care modificam legaturile existente astfel încât elementul curent sa fie legat decel nou, iar elementul nou va fi legat de urmatorul.

Pentru ca sa pot executa operat,iile de mai sus, trebuie sa t,in minte legaturi spre alte zone de memorie. Pointerii sunt

64 Chapter 5. Iterat, ia 3

Page 71: Programarea Calculatoarelor - utcluj.rousers.utcluj.ro/~visoft/teach/programareacalculatoarelor.pdfProgramarea Calculatoarelor, Versiune 0.32 1.2Desfas˘, urare 1.2.1Abstractizare

Programarea Calculatoarelor, Versiune 0.32

facut,i pentru asa ceva! Apoi, trebuie sa cuplez s, i informat,ia utila! (Pentru ca vreau sa memorez ceva, nu fac lista doarde dragul de a exista). Cum grupez date eterogene? Structuri!

Avem ingredientele pentru liste: Pointeri, structuri s, i pointeri la structuri. Pentru a putea manipula us, or listele s, i a nuscrie mult cod, ar fi ideal ca pointerul spre urmatoarea zona de memorie sa fie tot de tipul elementului listei.

Putem face acest lucru prin intermediul pointerilor pentru ca, la declarare/alocare de memorie pe compilator nu îlintereseaza tipul pointerului.

Mai exista o mica nota, structura trebuie sa aiba un nume, chiar daca folosim typedef.

Listing 5.6: Declarare de structura recursiva.

1 typedef struct __Lista{2 int payload;3 struct __Lista * next;4 } ListaInlantuita;

Linia 3 este locul unde se întâmpla magia. Mai întâi semantica. Tipul structura include s, i cuvântul cheie structurmat de numele structurii. În interiorul structurii trebuie folosit numele structurii pentru ca, înca suntem în interioruldefinit,iei typedef. Deoarece am inclus definit,ia structurii în typedef, în cod însa, putem folosi numele asociattipului.

Alocarea s, i dealocarea se poate face cu malloc s, i free. Atent,ie, este indicat ca la alocare sa init,ializat,i pointerulnext cu NULL.

Structura data ca exemplu cont,ine ca informat,ie utila un întreg. Putet,i pune evident s, i alte informat,ii.

Listele sunt cele mai simple structuri recursive. Putem construi arbori, grafuri, etc. Atent,ie, de obicei, pentru grafuriexista moduri de reprezentare mai utile (matrici de adiacent, a) pentru ca, algebra liniara funct,ioneaza bine pe astfel dematrici de adiacent, a. Mult,i algoritmi sofisticat,i se pot rezolva cu câteva operat,ii de algebra liniara.2

Observat,ie: În linia 3, daca nu scriam pointeri, compilatorul încerca sa decida cam câta memorie sa rezerve pentrustructura (pentru int, s, tie). Din cauza ca este o declarat,ie recursiva, va "intra" în ciclu infinit s, i va da eroare. Dacafolosim pointeri, s, tie câta memorie va trebui rezervata. Iar când va ajunge la expresii cu structura, aceasta este dejadefinita.

5.5.6 Reuniuni de date s, i structuri pe bit, i

Reuniuni

Tipul union permite crearea de vederi (views) asupra unei zone de memorie. Aducet,i-va aminte, în memorie stocamun pattern de bit,i. Tipul ne spune cum vom trata acest pattern de bit,i, în operat,ii. Acelas, i pattern de bit,i poate fi vazutca un întreg fara semn, ca un întreg cu semn sau ca o zona de char de lungime sizeof(int). Sau ca un numar învirgula mobila.

Sa începem cu un prim use case.

Listing 5.7: Tipul uniune

1 #include <stdio.h>2

3 typedef union{4 char str[4];5 unsigned int intreg;

(continues on next page)

2 Google foloses, te un algoritm specific pentru a determina paginile "importante" numit Page Rank. Matematic, acesta gases, te vectorii s, i valorileproprii ale unei matrici de adiacent,a. Cautat,i "page rank algorithm linear algebra". Înca o data, nu neglijat,i matematica!

5.5. Lucrul cu date structurate 65

Page 72: Programarea Calculatoarelor - utcluj.rousers.utcluj.ro/~visoft/teach/programareacalculatoarelor.pdfProgramarea Calculatoarelor, Versiune 0.32 1.2Desfas˘, urare 1.2.1Abstractizare

Programarea Calculatoarelor, Versiune 0.32

(continuare din pagina precedenta)

6 } MagicConstant;7

8 int main()9 {

10 MagicConstant mc;11 mc.intreg = 0x46445025;12 printf("Tip fisier: ");13 for(int i = 0; i < sizeof(mc.str); i++)14 printf("%c", mc.str[i]);15 printf("\nValoarea intreaga: %d\n", mc.intreg);16 return 0;17 }

Sa examinam codul de mai sus. Se declara o zona de memorie de lungime sizeof(MagicConstant). Acestpattern de bit,i îl putem vedea ca un s, ir de patru caractere (declarat,ia din linia 4) SAU ca un întreg fara semn.

La începutul fis, ierelor avem de obicei o grupare de bit,i care definesc tipul sau cont,inutul fis, ierului. A scrie în cod ocomparat,ie între un întreg s, i valoarea 1178882085 nu este ceva lizibil. Dar a face un strcmp între un s, ir de caracterecitit s, i "%PDF" este destul de clar! Mai multe astfel de constante, pe wikipedia.

În endianess-ul arhitecturii x86 datele se aliniaza de la adresa mai mica la adresa mai mare. În cazul de mai sus amdeclarat fix 4 valori, presupunând ca unsigned int ocupa fix 4 valori.

Daca am fi alocat 5 caractere, la setarea lui mc.intreg se seteaza primele 4 valori, datorita regulii de aliniere. Astfel,putem face în sigurant, a mc.str[4] = 0 s, i sa putem folosi direct mc.str în operat,ii cu s, iruri.

Mare atent,ie, la structuri, fiecare element este idependent, cu memoria lui. La uniuni, fiecare element "vede" aceeas, izona de memorie!

Câmpuri de bit, i

Un alt use case este "despachetara" regis, trilor de configurare a unor dispozitive fizice.

Comunicat,ia dintre diverse componente electronice se face de obicei pe un bus, folosind octet,i sau cuvine (word saudouble word în engleza)4

Dispozitivele neceista configurari înainte de folosint, a. Au diversi regis, tri de 1, 2, 5 bit,i. Mai mult,i astfel de regis, trisunt grupat,i în astfel de cuvinte sau octet,i de control. Funct,iile de comunicare cu bus-ul accepta doar întregi, nu bit,iindividuali.

Voi trebuie sa format,i astfel de s, iruri de bit,i s, i sa le trimitet,i dispozitivelor. Ca instrument,pâna acum s, tim doar deoperat,iile pe bit,i pentru a face acest lucru.

În tipul struct putem defini cât,i bit,i sa ocupe fiecare membru. Putem astfel "împacheta" mai multe variabile într-ozona de memorie mai mica. De exemplu, daca avem 4 variabile ce pot lua valori doar 0 sau 1, le putem împacheta peun octet în loc sa alocam 4 octet,i. Facem împachetarea de obicei NU ca sa economisim memorie ci ca sa putem accesas, i construi us, or regis, tri de configurare pentru periferice.

Listing 5.8: Tipul strcut cu specificat,ii de bit,i

1 typedef struct{2 unsigned char on_off :1;3 unsigned char viteza :3;4 unsigned char precizie :4;5 } _structRegistru;

4 Un cuvânt denumes, te un tip de 16 sau 32 de bit,i. Este mai mult o convent,ie de exprimare decât un tip bine stabilit (ca s, i byte care tot timpuldenumes, te o valoare pe 8 bit,i)

66 Chapter 5. Iterat, ia 3

Page 73: Programarea Calculatoarelor - utcluj.rousers.utcluj.ro/~visoft/teach/programareacalculatoarelor.pdfProgramarea Calculatoarelor, Versiune 0.32 1.2Desfas˘, urare 1.2.1Abstractizare

Programarea Calculatoarelor, Versiune 0.32

Aici avem alocat,i 8 bit,i în 3 câmpuri:

7 6 5 4 3 2 1 0

precizie viteza on_o�

Atent,ie, ca s, i la union alocarea se face de la adresa mai mica la adresa mai mare. De aceea, câmpul on_off este laadresa de bit,i zero.

Acum, combinam conceptul de union cu cel de structura pe bit,i:

Listing 5.9: Împart,irea unui întreg în câmpuri de bit,i

1 #include <stdio.h>2

3 typedef struct{4 unsigned char on_off :1;5 unsigned char viteza :3;6 unsigned char precizie :4;7 } _structRegistru;8

9 typedef union{10 _structRegistru campuri;11 char valoare_impachetata;12 } Registru;13

14 int main()15 {16 Registru r;17 r.valoare_impachetata = 0;18 r.campuri.on_off = 1;19 r.campuri.viteza = 1;20 r.campuri.precizie = 15;21

22 printf("Valoare impachetata: %d", r);23

24 return 0;25 }

Va afis, a valoarea zecimala 243. În binar, este 1111 0011.

Folsind tipul Registru putem accesa individual câmpurile s, i citi/trimite la periferic registrul de configurare.

Important: Exista reguli de aliniere a datelor. Alinierea implica faptul ca un câmp nu poate fi declarat astfel încâtsa treaca peste aliniere. El va începe la adresa imediat urmatoare chiar daca exista loc pentru cât,iva bit,i în unitateaanterioara de aliniere. Numarul de bit,i pe care se face alinierea depinde de compilator s, i arhitectura. De obicei, 32 sau64 bit,i. Verificat,i înainte de a planifica sa folosit,i o astfel de structura.

5.5. Lucrul cu date structurate 67

Page 74: Programarea Calculatoarelor - utcluj.rousers.utcluj.ro/~visoft/teach/programareacalculatoarelor.pdfProgramarea Calculatoarelor, Versiune 0.32 1.2Desfas˘, urare 1.2.1Abstractizare

Programarea Calculatoarelor, Versiune 0.32

5.6 Functii

Având cunos, tint,e despre toolchain, pointeri s, i module intram sa vedem câteva not,iuni mai avansate de lucru cu funct,ii.

Vom vorbi despre recursivitate, pointeri la funct,ii, callback-uri.

5.6.1 Recursivitatea

La pointeri am vazut ca o variabila capabila sa stocheze o adresa ocupa o zona de memorie dependenta doar dearhitectura pentru care se compileaza codul sursa. NU depinde de tipul spre care pointeaza variabila. Tipul apare încalcule de adrese, în lucru cu cont,inutul zonei de memorie, etc.

Apoi, at,i vazut ca, la compilare, atunci cand se apeleaza o funct,ie, compilatorul scrie o adnotare ca adresa de salt.

Aceasta adnotare este generata pe baza unor reguli ce depind s, i de numele funct,iei.

Combinând, este perfect legal sa avem o funct,ie care se apeleaza pe ea însas, i.

Aceasta tehnica de programare se numes, te recursivitate. Exista algoritmi care se descriu natural prin metode recursive.

Avem not,iunile de baza, s, tim cum e posibil sa se compileze un astfel de cod, s, tim (din iterat,ia 2) ca variabilele localesunt alocate s, i init,ializate la intrarea în funct,ie, s, tim ca fiecare apel al funct,iei este independent de celelalte apeluri.S, tim ca primul lucru care trebuie sa îl facem la un apel recursiv e sa ne oprim.

Putem trece direct la exemple care "unesc" toate aceste concepte.

Problema: Scriet,i cod care sa dea valorile funt,iei 𝑓(𝑥) = 𝑓(𝑥 − 1) + 5, 𝑓(0) = 0. Presupunem ca nu s, tim sa orezolvam analitic.

Listing 5.10: Exemplu simplu de apel recursiv.

1 #include <stdio.h>2 int func(int x){3 if (x <= 0)4 return 0;5 else6 return func(x - 1) + 5;7 }8

9 int main()10 {11 for(int i = 0; i < 10; i++)12 printf("f(%d) = %d\n", i, func(i));13 return 0;14 }

La linia 3, verificam daca nu am depas, it domeniul de definit,ie sau daca nu am atins condit,ia de oprire. Atent,ie, amscris <= în loc de == pentru robustet,e. Daca cumva se apeleaza cu func(-1), s, i operat,ia era de egalitate, intram înrecursivitate infinita vazând foarte repede celebrul mesaj de eroare.

Complicat? Deocamdata nu. Haidet,i sa afis, am nis, te valori intermediare sa înt,elegem subtilitatea stocarii variabilelorlocale.

Listing 5.11: Exemplu de apel recursiv, cu multe mesaje de depanare

1 #include <stdio.h>2 int func(int x){3 printf("x = %d, enter function\n", x);

(continues on next page)

68 Chapter 5. Iterat, ia 3

Page 75: Programarea Calculatoarelor - utcluj.rousers.utcluj.ro/~visoft/teach/programareacalculatoarelor.pdfProgramarea Calculatoarelor, Versiune 0.32 1.2Desfas˘, urare 1.2.1Abstractizare

Programarea Calculatoarelor, Versiune 0.32

(continuare din pagina precedenta)

4 if (x <= 0)5 return 0;6 printf("x = %d, after halting condition\n", x);7 int result = 5;8 printf("x = %d, result = %d, after declaring result\n", x, result);9 int fx_minus_one = func(x - 1);

10 printf("x = %d, result = %d, fx_minus_one=%d, after reccurent call\n", x, result,→˓ fx_minus_one);

11 result += fx_minus_one;12 printf("x = %d, result = %d, fx_minus_one=%d, just before return\n", x, result,

→˓fx_minus_one);13 return result;14 }15

16 int main()17 {18 // for(int i = 0; i < 10; i++)19 // printf("f(%d) = %d\n", i, func(i));20 printf("f(%d) = %d\n", 3, func(3));21 return 0;22 }

Aici avem afis, ata ies, irea în consola:

x = 3, enter functionx = 3, after halting conditionx = 3, result = 5, after declaring resultx = 2, enter functionx = 2, after halting conditionx = 2, result = 5, after declaring resultx = 1, enter functionx = 1, after halting conditionx = 1, result = 5, after declaring resultx = 0, enter functionx = 1, result = 5, fx_minus_one=0, after reccurent callx = 1, result = 5, fx_minus_one=0, just before returnx = 2, result = 5, fx_minus_one=5, after reccurent callx = 2, result = 10, fx_minus_one=5, just before returnx = 3, result = 5, fx_minus_one=10, after reccurent callx = 3, result = 15, fx_minus_one=10, just before returnf(3) = 15

Citit,i cu atent,ie s, i observat,i ca variabila result îs, i pastreaza valoarea veche atunci cînd codul se întoarce din apelulrecursiv. Aceasta este esent,a înt,elegerii recursivitat,ii!

Scriem mai clar:

int functie(int k){// ....int n = 5 * k; // Aici n ia o anumita valoare, fie ea 10functie( k / 2 ); // Apel recursiv, se va aloca pe stiva un nou k si un nou nn + 2; // Aici n are tot valoarea 10, indiferent ce s-a întâmplat

→˓în apelul recursiv!!!!// ....

}

De ret,inut:

5.6. Functii 69

Page 76: Programarea Calculatoarelor - utcluj.rousers.utcluj.ro/~visoft/teach/programareacalculatoarelor.pdfProgramarea Calculatoarelor, Versiune 0.32 1.2Desfas˘, urare 1.2.1Abstractizare

Programarea Calculatoarelor, Versiune 0.32

• Primul lucru în funct,ia recursiva este condit,ia de oprire. Ea trebuie sa fie robusta

• Variabilele locale din rularea curenta NU sunt afectate de rularile recursive. Daca vrem sa modificam valori dinurmatorul apel, singura metoda e de a modifica valorile reale cu care se va efectua urmatorul apel recursiv. Lafel, singurul mod de a afecta rulari anterioare este de a returna o valoare.

• Trebuie sa avem o variabila sau un parametru care descres, te la fiecare apel. Altfel, intram în recursivitate infinita.

Daca ar fi sa rezolvam analitic problema, putem constata us, or ca expresia 5 * (x - 1) rezolva direct problema.

Problema Una clasica, s, irul lui Fibonacci.

Sa se gaseasca valorile lui f : 𝑓(𝑛) = 𝑓(𝑛− 1) + 𝑓(𝑛− 2) unde 𝑓(1) = 1 s, i 𝑓(0) = 0

Abordat,i iterativ (ca metodologie) rezolvarea! Mai întâi facet,i pur recursiv, (Atent,ie, nu dat,i numere mari la n! Sub 5)

Apoi observat,i ca daca începet,i calculul de la 0 la n putet,i evita cu totul recursivitatea, (s, i putet,i rezolva problemarelativ repede (ca timp de execut,ie) pentru valori mai mari ale lui n, sute, mii)

Apoi aflat,i radacinile polinomului caracteristic progresiei aritmetice (sau cautat,i pe Google cum arata solut,ia) s, irezolvat,i problema printr-o expresie analitica. Vedet,i cât de mari sunt numerele pentru care putet,i calcula formulalui Fibonacci. Valori pentru care s, i solut,ia anterioara avea dificultat,i.

Important: La fiecare algoritm sau bucata de cod putem sa îi estimam o durata de timp, în funct,ie de câte calcule sefac. Funct,ia recursiva pe care at,i rezolvat-o la Fibonacci face foarte multe calcule pentru o valoare data a lui n.

La "Structuri de date s, i algoritmi" vet,i învat,a despre complexitat,i s, i timpi exponent,iali de rulare. Deocamdata ret,inet,ica este posibil sa rezolvat,i o problema în timp de execut,ie mai mic sau mai mare, în funct,ie de algoritmul abordat.

Important: Atunci când lucrat,i pe dispozitive embedded EVITAT, I RECURSIVITATEA CU ORICE PRET, .

Ca idee, orice algoritm recursiv uzual întâlnit în medii embedded poate fi "liniarizat" sau macar implementat fara apelrecursiv. Uneori trebuie sa t,inem explicit o strucutra de date de tip stiva sau coada.

5.6.2 Pointerii la funct, ii

S, tim ca funct,iile sunt de fapt nis, te bucat,i de cod, la o anumita adresa, care pot fi apelate de oridunde din program.

Aceste adrese sunt fixate la link-editare. Deasemenea, avem s, i pointeri care pot stoca o adresa arbitrara din memorie!

Bine at,i venit la pointeri la funct,ii! Unde avem variabile special facute sa poata stoca adrese spre cod S, I (cel maiimportant) sa pot apela codul de acolo, ca un apel de funct,ie!

Aceste instrumente aparent exotice sunt gasite frecvent. De la mediul embedded pâna la programarea web. Nu suntfoarte greu de priceput s, i utilizat, conceptul de pointer la funct,ie este gasit în majoritatea (poate în toate) limbajeleimperative iar codul câs, tiga o flexibilitate mare.

Din pacate în C sintaxa este mai greoaie s, i stârnes, te confuzie, de aceea le-am lasat pentru Iterat,ia 3 (moment în carese presupune ca stapânit,i bine pointerii s, i funct,iile).

Pointerii la funct,ii sunt folosit,i pentru a trimite funct,ii ca parametru altor funct,ii care vor apela la rândul lor acestefunct,ii.

70 Chapter 5. Iterat, ia 3

Page 77: Programarea Calculatoarelor - utcluj.rousers.utcluj.ro/~visoft/teach/programareacalculatoarelor.pdfProgramarea Calculatoarelor, Versiune 0.32 1.2Desfas˘, urare 1.2.1Abstractizare

Programarea Calculatoarelor, Versiune 0.32

Motivat, ie

Înainte sa începem cu CUM, ne uitam un pic la DE CE.

Sa presupunem ca avem un algoritm sofisticat de gasire a minimului unei funct,ii, algoritm ce automat calculeazaderivatele, face put,ini pas, i pâna la solut,ie etc. Singura cerint, a e ca funct,ia f sa poata fi evaluata pentru orice punct x.

Daca ar fi sa scriet,i codul, ar trebui sa avet,i un apel la funct,ia f. Daca modificat,i funct,ia t,inta, tot codul trebuierecompilat. Asta cumva impune o limitare la modul cum se poate coda s, i împacheta codul pentru minimizarea funct,iei.

Clientul ar trebui sa recompileze codul de fiecare data când schimba funct,ia. Ok, nimic nemaivazut.

Acum clientul vrea sa optimizeze (gasirea minimului) mai multe funct,ii! Ce face? Codul de minimizare este scrispentru o singura funct,ie! Copy paste? Ce se întâmpla daca gasim un bug în metoda de minimizare s, i retrimitembiblioteca spre client? Cât de us, or îi va fi sa modifice?

Cum ar fi daca, ar exista un mecanism mai general, care sa apeleze o funct,ie oarecare, de tip float f(float) defiecare data când e nevoie? S, i acea funct,ie f sa fie un parametru al funct,iei noastre de minimizare?

Alt caz:

Spuneam la început ca procesorul are multe periferice. El nu asculta periodic daca sunt modificari sau nu, ci estenotificat de dispozitive atunci când apar modificari în exterior. Apoi notificarea merge la sistemul de operare s, i apoi lanoi în program (daca dorim).

Pe dispozitivele embedded procesorul trebuie sa s, tie ce bucata de cod apeleaza în momentul în care apare o astfel demodificare în mediul exterior. Pe OS-urile moderne, sistemul de operare trebuie sa s, tie ce bucata de cod trebuie apelatacând se întâmpla anumite modificari în mediu. Mai mult, în funct,ie de logica programului codul de raspuns se poatemodifica în timpul rularii programului. Opt,iunea de a recompila totul, nu exista.

Pentru ambele situat,ii pointerii la funct,ii rezolva elegant problema.

Definit, ia unui pointer la funct, ii

Definirea unei variabile care poate ret,ine un pointer la o funct,ie se face:

tip_returnat (* nume_variabila)(lista_tipurilor_parametrilor_formali)

Din nou, convent,ia ca tipul este în stânga numelui variabilei es, ueaza. Variabila nume_variabila este un pointer.Ea poate stoca adresa unei funct,ii, dar, atent,ie, a unei funct,ii ai caror lista de parametri formali s, i tip returnat suntIDENTICI cu cei declarat,i la declararea variabilei.

Apelul unui astfel de pointer se face ca s, i cum pointerul ar fi numele unei funct,ii normale:nume_variabila(parametri)

Este perfect valida s, i expresia (*nume_variabila)(parametri). În caz ca nu erau treburile suficient decomplicate.

Expresia de declarare a unei variabile spre o funct,ie poate aparea (s, i de fapt acesta este scopul existent,ei lor) si caparametru formal al altei funct,ii.

Pot fi declarate tabele de pointeri la funct,ii, elemente a structurilor, etc.

Am dat exemplu de minimizare a unei funct,ii. Avem mai jos exemplu de implementare pentru o metoda ce cautaminimul între doua puncte. Este luata de aici: https://en.wikipedia.org/wiki/Golden-section_search

Listing 5.12: Exemplu de pointer la funct,ie

#include <stdio.h>#include <math.h>

(continues on next page)

5.6. Functii 71

Page 78: Programarea Calculatoarelor - utcluj.rousers.utcluj.ro/~visoft/teach/programareacalculatoarelor.pdfProgramarea Calculatoarelor, Versiune 0.32 1.2Desfas˘, urare 1.2.1Abstractizare

Programarea Calculatoarelor, Versiune 0.32

(continuare din pagina precedenta)

const double GOLDEN_RATIO = (sqrt(5) + 1) / 2;

double minim(double (*f)(double), double left, double right, double epsilon){double a, c, d, err;a = (right - left) / GOLDEN_RATIO;c = right - a;d = left + a;err = 1;while (err > epsilon){

if (f(c) < f(d))right = d;

elseleft = c;

a = (right - left) / GOLDEN_RATIO;c = right - a;d = left + a;err = fabs(c - d);

}return (right + left) / 2;

}

double f1(double x){return x * x;

}

double f2(double x){return x * x + 2 * x - 8;

}

int main(){

double x, y;x = minim(f1, -100, 100, 1e-5);y = f1(x);printf("Minimum x=%.3f f1(%.3f) = %.3f\n", x, x, y);

x = minim(f2, -100, 100, 0.001);y = f2(x);printf("Minimum x=%.3f f2(%.3f) = %.3f\n", x, x, y);

return 0;}

Exista în biblioteca standard funct,ii care implementeaza algoritmi cunoscut,i s, i care defera calculele ce t,in de dateleutilizatorului catre o funct,ie utilizator. Acest mecanism se numes, te callback.

Cel mai bun exemplu este funct,ia qsort din stdlib.h. Cautat,i s, i studiat,i documentat,ia. Vet,i observa ca funct,iautilizator va trebui sa accepte doi pointeri spre elemente din tabelul ce se vrea sortat s, i va trebui sa returneze ordineacelor doua elemente. Des, la callback-uri, apelantul (sistemul de operare sau o funct,ie din biblioteca) va trimitefunct,iei utilizator date setate în prealabl de catre utilizator. Aceste date ajuta codul din funct,ie sa poata sa îndeplineascasarcinile.

Un alt exemplu de callback este într-o interfat, a grafica (fie ea web) când trebuie sa facet,i ceva ca raspuns la act,iunileutilizatorului sau a ret,elei. De obicei, interfat,a grafica e implementata cu ajutorul unui framework. Acestuia îi spunet,ice act,iune sa faca, (ce funct,ie sa apeleze, numita funct,ia de callback) când userul apasa pe un buton. Putet,i sa îi dat,i s, io variabila, variabila pe care framework-ul sa va o trimita în momentul apelului de callback. framework-ul va memorala fiecare buton, ce informat,ie utilizator i-a fost asignata.

72 Chapter 5. Iterat, ia 3

Page 79: Programarea Calculatoarelor - utcluj.rousers.utcluj.ro/~visoft/teach/programareacalculatoarelor.pdfProgramarea Calculatoarelor, Versiune 0.32 1.2Desfas˘, urare 1.2.1Abstractizare

Programarea Calculatoarelor, Versiune 0.32

Daca avet,i zeci de butoane, acest lucru este foarte util, cu o singura bucata de cod putet,i gestiona mai multe evenimente.

Alt exemplu: Vret,i sa creat,i o pagina web care afis, eaza multe poze, de pe internet. Pentru ca userul sa poata vedeainformat,ia deja incarcata, instruit,i framework-ul ce poze sa încarce s, i ce funct, ie sa apeleze când poza e încarcata.Daca facet,i astfel încât funct,iei respective sa i se transmita s, i locat,ia unde sa deseneze poza încarcata, at,i reus, it, cuun pic de cod, sa creat,i o interfat, a grafica moderna, fluida, care nu se blocheaza as, teptând date de pe internet s, i careafis, eaza datele imediat cum ele sunt disponibile.

Simplu s, i elengat, DAR trebuie sa fit,i confortabili cu mecanismul de callback. Daca vet,i lucra în aplicat,ii web s, idistribuite, vet,i folosi acest mecanism de callback la fel de des ca s, i declararea de variabile în C.

5.7 Strategii de programare

5.7.1 Optimizari s, i "mai optim"5

Am vazut la s, irul Fibonacci ca exista trei strategii de rezolvare. Cea recursiva este executata lent iar pentru valori maimari nu se pot stoca apelurile pe stiva. Varianta "smart" când efectuam calculele de la 0 la n, este mult mai rapida!Totus, i, pentru valori mari ale lui n, execut,ia dureaza un pic. Cea mai rapida rezolvare (ca timp de execut,ie) estemetoda analitica în care am folosit proprietat,ile seriei aritmetice.

Important: În general solut,iile matematice permit rezolvari mult mai elegante s, i stabile decât rezolvarile algoritmice.Cunos, tint,ele de matematica sunt esent,iale pentru a rezolva probleme, mai ales în lumea hardware. În aplicat,ii web,treburile sunt mai simple pâna când sistemul trebuie sa scaleze. Sau, sa executat,i operat,ii relativ simple pe volumeimense de date. Iaras, i, anumite ramuri ale matematicii va permit sa venit,i cu solut,ii rapide, elegante s, i robuste.

O lect,ie ce se desprinde din s, irul Fibonacci este ca abordarile algoritmice, folosirea de structuri de date potriviteproblemei, ajuta foarte mult. Cunos, tint,ele fundamentale, însa, asigura solut,ia optima! Pentru problemele"nerezolvabile" analitic, tot matematica poate da limite (sau regimul de lucru) la solut,iile de aproximare.

Sub nici o forma "optimizarile" la nivelul limbajului de programare nu pot compensa pentru un algoritm prost. Dacanu exista cerint,e stricte de performant, a, sacrificat,i un pic de performant, a pentru claritate. De exemplu, avet,i o expresielunga. Sparget,i-o s, i calculat,i-o în mai multe etape, chiar daca alocat,i degeaba 4-5 variabile în plus.

Hack-urile s, i trick-urile la nivel de cod (s, i cod mas, ina) le aplicat,i doar dupa ce suntet,i siguri ca solut,ia voastra esteoptima (cu sens superlativ) din punct de vedere algoritmic.

5.7.2 Cod curat

În industrie vet,i scrie cod în proiecte mari, gigantice. Trebuie sa putet,i abstractiza funct,ionalitat,i fara sa va ratacit,i încod, citind la nesfârs, it. Deasemenea, codul vostru trebuie sa fie us, or de citit s, i înt,eles, fara capcane pentru cel carecites, te. NU va bazat,i pe comentarii, acestea de obicei nu se modifica odata cu codul.

Principiile care fac un cod sa fie us, or de înt,eles, us, or de folosit s, i fara bug-uri se numesc "Clean code" sau cod curat.

Exista multe instrumente de monitorizare s, i testare a codului. Codul unui proiect cres, te s, i se modifica constant. Nu vasperiat,i ca nu iese totul perfect din prima încercare.

Important: Codul este mai mult citit decât scris. De multe ori cel care va citi codul are as, teptari sau cunos, tint,ediferite fat, a de voi. Scriet,i cod cât mai clar! Este foarte important sa transmitet,i explicit intent,ia voastra. Se accepta

5 Teoretic, gramatical, cuvântul optim este superlativ. Nu exista construct,ia mai optim. Dar cum limba este o fiint,a vie, se mai accepta înconversat,ii colocviale. "Fa codul ala sa fie mai optim"

5.7. Strategii de programare 73

Page 80: Programarea Calculatoarelor - utcluj.rousers.utcluj.ro/~visoft/teach/programareacalculatoarelor.pdfProgramarea Calculatoarelor, Versiune 0.32 1.2Desfas˘, urare 1.2.1Abstractizare

Programarea Calculatoarelor, Versiune 0.32

unele scurtaturi, ca s->v[i] dar NU putchar(c>31^c>126?c:46)6. Exista o cutuma în domeniul inginerieisoftware: "Always code as if the guy who ends up maintaining your code will be a violent psychopath who knowswhere you live. Code for readability."7. Multe construct,ii perfect legale în C au fost scoase din acest curs tocmaipentru a evita folosirea lor. Regulile date însa, va permit sa "descifrat,i" un astfel de cod.

La colocvii s, i examen de obicei un punct se câs, tiga scriind cod clar, cu nume de variabile explicite s, i relevante.

S, i cum nimic pe lumea asta nu e simplu, exista cazuri în care trebuie sa facet,i înmult,iri s, i împart,iri cu 2 folosindoperat,ii de deplasare pe bit,i sau interschimbare de variabila cu XOR. De obicei când avet,i un controller cu putere micade calcul. Dar ATENT, IE! Codul "magic" va fi documentat s, i va avea teste scrise care sa asigure corectitudinea.

Sintagma de "cod curat" acopera mai mult decât alegerile de nume de variabila s, i expresii. Acopera s, i modul în caretextul este organizat în fis, ier s, i placerea cu care ochiul aluneca pe text. Un cod bine scris s, i bine "pus în pagina" seexplica singur.

Este important sa va adaptat,i specificului "local" al codului pe care îl editat,i. Este foarte deranjant sa cites, ti un texts, i în mijlocul lui sa vezi o formatare diferita. Sau o culoare diferita. Ît,i pui întrebari despre eventuala semnificat,ie, ît,isare în ochi pasajul respectiv. Adica ai pierdut timp s, i energie pentru ceva ce nu trebuia sa consume timp s, i energie.

Doar daca exista indicat,ii exprese în a adera la un nou mod de a formata/scrie codul, scriet,i cod formatat "nou" în fis, ier"vechi".

Sa vedem foarte pe scurt, câteva principii calauzitoare.

DRY: Don't Repeat Yourself.

Cel mai simplu principiu de respectat. Ai o operat,ie mai complexa? Implementeaza o funct,ionalitate a proiectului?(un feature) O foloses, ti în doua locuri? Fa o funct,ie sau un modul! Apeleaz-o acolo unde trebuie folosita!

Aici este necesar cunoas, terea buna a instrumentelor avute la dispozit,ie pentru a putea scrie cod modular, codparametrizabil, cod flexibil.

Constantele "magice" intra tot aici. De la banalul pi pâna la valori ale regis, trilor sau alte constante. Folosit,i din plin#define!

Agregarea funct, ionalitat, ii s, i responsabilitat, i unice pentru funct, ii

Nu va ferit,i sa creat,i module s, i sa "rupet,i" codul în bucat,i mai mici. Un calcul matematic nu are ce cauta lânga un codce comunica cu exteriorul. Un cod care seteaza memoria proaspat alocata, are sens sa fie scris imediat sub codul dealocare.

Daca însa alocarea e mai complicata, init,ializarea are mai multe opt,iuni, atunci cele doua funct,ionalitat,i vor trebuidespart,ite.

KISS: Keep it simple s... s, i YAGI: You Ain't Gonna need It!

Nu va apucat,i cu solut,ii ultra complexe s, i abstracte doar ca vi se pare interesant. Statistic, sunt s, anse ca, specificat,iilepentru care at,i scris codul sa se modifice. Cât timp codul este relativ curat, nu facet,i implementari complexe decâtatunci când o va cere situat,ia. Nis, te funct,ii relativ slab cuplate pot trai mult s, i bine în acelas, i modul (sa numarat,i decâte ori vet,i denumi un modul utils) pâna când devine evident ca trebuie separate.

Termeni ca refactoring s, i technical debt sunt folosit,i pentru a descrie astfel de situat,ii. Din nou, abordarea iterativa,lean, metodologia agile, va ajuta sa putet,i scoate un cod bun, curat, în bugetul de timp stabilit.

Din pacate a scrie un cod curat este mai dificil decât a învat,a prioritatea operatorilor. Simt,ul s, i intuit,ia de a scrie codcurat vine odata cu experient,a s, i vine odata cu gres, elile facute. Fiecare principiu (s, i altele care intra tot la clean code)exista pentru ca oamenii au facut mereu s, i mereu aceleas, i gres, eli.

6 Exemplu luat la întâmplare de pe StackOverflow, sect,iunea de Code Golf, link: https://codegolf.stackexchange.com/a/11339 . Scopul vostruNU este sa scriet,i cod cât mai scurt s, i "inteligent".

7 Link aici: https://groups.google.com/forum/#!msg/comp.lang.c++/rYCO5yn4lXw/oITtSkZOtoUJ sau aici: https://stackoverflow.com/a/878436/2662454.

74 Chapter 5. Iterat, ia 3

Page 81: Programarea Calculatoarelor - utcluj.rousers.utcluj.ro/~visoft/teach/programareacalculatoarelor.pdfProgramarea Calculatoarelor, Versiune 0.32 1.2Desfas˘, urare 1.2.1Abstractizare

Programarea Calculatoarelor, Versiune 0.32

As, a ca va recomand sa va implicat,i în cât mai multe proiecte, cât mai complexe! Cautat,i locuri s, i proiecte de undeputet,i învat,a.

Succes!

5.7. Strategii de programare 75

Page 82: Programarea Calculatoarelor - utcluj.rousers.utcluj.ro/~visoft/teach/programareacalculatoarelor.pdfProgramarea Calculatoarelor, Versiune 0.32 1.2Desfas˘, urare 1.2.1Abstractizare

Programarea Calculatoarelor, Versiune 0.32

76 Chapter 5. Iterat, ia 3

Page 83: Programarea Calculatoarelor - utcluj.rousers.utcluj.ro/~visoft/teach/programareacalculatoarelor.pdfProgramarea Calculatoarelor, Versiune 0.32 1.2Desfas˘, urare 1.2.1Abstractizare

CHAPTER 6

Limbajul s, i mediul Python

6.1 Introducere

Python este un limbaj imperativ, obiectual, funct,ional. Permite folosirea tuturor celor trei paradigme, fara nici oproblema. Are ca aplicat,ii de la IoT (merge perfect pe un Raspberry) la Inteligent, a artificiala (câteva framework-uri învoga acum sunt bazate pe python) la aplicat,ii web.

Ne vom concentra aici un pic pe câteva elemente de sintaxa s, i pe partea de calcul numeric a limbajului (de fapt penis, te biblioteci matematice).

Limbajul Python este un limbaj interpretat. Exista un interpretpr care executa pas cu pas fiecare instruct,iune. Astaînseamna ca:

• Nu am executabil

• Fara "ajutor", este relativ lent

• Toolchain-ul s, i mediul de dezvoltare conteaza foarte mult

Limbajul este foare expresiv, "Hello World" e o singura linie.

Listing 6.1: Hello World în Python

print("Hello World")

Absolut nimic mai mult decât linia de sus nu trebuie sa scriet,i pentru a face primul pas. Nu doar lipsa diverselorelemente (acolade, ; etc) îl fac expresiv ci s, i faptul ca multe structuri de date s, i algoritmi de baza sunt integrat,i înlimbaj.

Încercat,i exemplul de mai jos, în C.

77

Page 84: Programarea Calculatoarelor - utcluj.rousers.utcluj.ro/~visoft/teach/programareacalculatoarelor.pdfProgramarea Calculatoarelor, Versiune 0.32 1.2Desfas˘, urare 1.2.1Abstractizare

Programarea Calculatoarelor, Versiune 0.32

Listing 6.2: Sortarea unei liste de cuvinte

nume = ["Pop", "Ion", "Mircea"]print(sorted(nume))

Alocare, init,ializare, sortare, afis, are, în doua linii de cod. Mergea s, i sa înghesui lista direct în print dar nu aratabine.

Limbajul este tot case-sensitive ca s, i C-ul. Avem funct,ii, dar avem s, i cod care traies, te în exteriorul funct,iilor. El esteexecutat de interpretor în momentul în care este întâlnit.

În limbajul C, pentru fiecare variabila trebuie specificat tipul. Acesta era legat de nume s, i nu se putea modifica peparcursul execut,iei. Fiecare tip avea reguli stricte de folosint, a s, i interact,iune.

În Python avem reguli stricte (nu pot aduna o lista la un întreg) dar tipul variabilei nu este definit static.

Adica, o variabila poate sa îs, i schimbe tipul pe parcursul existent,ei programului. Asta deoarece o variabila nu estedecât o adresa de memorie. Interpretorul gestioneaza aceste adrese, pastrând informat,ie de tip, de disponibilitate,alocând s, i dealocând pentru noi, automat.

6.2 Elemente de sintaxa

Nu exista cod urât scris în Python. Asta pentru ca indentarea (numarul de spat,ii libere de la marginea stânga a rândului)t,ine loc de acolade. Hai sa vedem:

Listing 6.3: Numere pare, edit,ia pe Python

i = 0while i < 10:

if i % 2 == 0:print("{} ".format(i))

i += 2

Observat,i lipsa acoladelor. Totus, i, dupa condit,ia de la if s, i while vine un caracter : s, i apoi urmatorul bloc deinstruct,iuni. Nu exista ; dupa fiecare instruct,iune. Se presupune ca instruct,iunea se termina pe rândul curent.

Expresiile sunt la fel, afis, area foarte asemanatoare (avem acolade în loc de % ca specificator de format). Dar unde estefor-ul? De ce am scris while? Vom vedea dupa ce facem câteva structuri de date.

Recent, s-a introdus posibilitatea de a scapa s, i de acel .format din afis, are. Se poate "grupa" totul mai compact:

print(f"{i} ")

Am scris caracterul f lipit de ghilimele iar în specificatorul de format am scris numele variabilei. Folosit,i ce notat,ieva place. Atent,ie, ultima varianta aratata este doar din versiunea 3.6.

Indexarea în liste, tabele s, i dict,ionare (vedem imediat) se face cu []. Apelul de funct,ii se face tot cu paranteze.

Includerea de alte biblioteci se face folosind cuvântul cheie import. Exemplu: import numpy.

78 Chapter 6. Limbajul s, i mediul Python

Page 85: Programarea Calculatoarelor - utcluj.rousers.utcluj.ro/~visoft/teach/programareacalculatoarelor.pdfProgramarea Calculatoarelor, Versiune 0.32 1.2Desfas˘, urare 1.2.1Abstractizare

Programarea Calculatoarelor, Versiune 0.32

6.3 Structuri de date

În Python cam tot ceea ce manipulat,i este de fapt o referint, a la o zona de memorie gestionata de interpretor. De aceea,daca modificat,i o structura de date în interiorul unei funct,ii, modificarea va fi vizibila în exterior.

Exista în Python conceptul de tipuri de date imutabile s, i mutabile. Interpretorul "pazes, te" zona de memorie a variabileiimutabile s, i împiedica modificarea.

Sa vedem câteva tipuri foarte uzuale de date. Sarim peste clasicii întregi, virgula mobila, constante caracter (des, i las, iruri discut,ia e mai complicata cu standardul UTF-8 s, i internat,ionalizarea). Ele funct,ioneaza as, a cum va as, teptat,i.

Exista în Python multe tipuri standard de date compuse. Ele au, pe lânga memorie s, i anumite funct,ii atas, ate (cod).De exemplu, avem un s, ir de caractere, nume = "Popescu". Citind documentat,ia vedem ca s, irurile de caractere,string-urile au multe funct,ii utile. Una, de exemplu, este upper. Transforma literele mici în litere mari:

Listing 6.4: Exemplu de lucru cu obiecte

1 nume = "Popescu"2 nume = nume.upper()3 print(nume)

Observat,i linia 2, expresia nume.upper() Daca ar fi sa citim cod C, am zice ca nume arata ca o structura, uppereste un câmp al structurii. Acest câmp ar trebui sa fie de tip pointer la o funct,ie pentru ca, pe el se aplica operatorulapel de funct,ie. Nimic mai adevarat! Sunt totus, i câteva remarci. Numele upper este întradevar o funct,ie, numeleunei bucat,i de cod. Dar aceasta este speciala, pentru ca este "lipita" de tipul de date s,ir de caractere. Ea vaprimi în cod, ca prim parametru, "adresa" de memorie a variabilei de pe care a fost apelata (adica, nume).

Pe scurt, ceea ce am enunt,at mai sus este conceptul de clasa (tip de date care au atas, at cod) s, i obiect (O instant,iere, ovariabila a acelui tip). NU intram în mai multe detalii. Trebuie sa s, tit,i ca aproape tot ce operat,i în Python este o clasarespectiv un obiect al unei clase. S, i ca ele au funct,ii atas, ate (metode). Aceastea se aplica pe obiectul "de pe care" s-afacut apelul.

Revenind la codul anterior, dupa cum va as, teptat,i, pe ecran se va afis, a POPESCU.

Am avut contact cu tipul string, str. Cautat,i sa vedet,i câte funct,ii utile are!

6.3.1 S, ir de caractere

Am vazut ca un s, ir de caractere se pastreaza într-un obiect de tip str s, i ca putem face anumite operat,ii specifice s, irurilorde caractere.

S, irurile de caractere sunt imutabile. Odata create NU se mai pot modifica.

6.3.2 Liste

Vrem sa memoram un tabel de numere. Nimic mai simplu. Scriem la fel ca s, i în C, partea de init,ializare a tabelelor.Doar ca în loc de acolade, punem paranteze drepte: lista = [1, 2, 3]. Fiecare element din lista este de fapt oreferint, a spre locat,ia de memorie a elementului. Putem exploata s, i sa avem liste eterogene:

lista = [1, "Popescu", [3, 4, 5]]print(lista)print(lista[2])

Nu va recomand decât dacâ tot codul vostru va "consuma" astfel de date. Daca adunat,i câte un întreg la fiecare elemental listei de mai sus, se vor produce erori.

6.3. Structuri de date 79

Page 86: Programarea Calculatoarelor - utcluj.rousers.utcluj.ro/~visoft/teach/programareacalculatoarelor.pdfProgramarea Calculatoarelor, Versiune 0.32 1.2Desfas˘, urare 1.2.1Abstractizare

Programarea Calculatoarelor, Versiune 0.32

Lista este un tip mutabil. Daca trimit lista mea într-o funct,ie, acolo se modifica de exemplu elementul de pe pozit,ia 2,modificarea "se va vedea" în exterior.

6.3.3 Tuple

Ele sunt mai speciale în Python dar le putet,i gândi ca un fel de liste dar care nu se pot modifica. Sintaxa este la feldoar ca avem paranteze rotunde () în loc de paranteze drepte.

tupla = (1, "Popescu", [3, 4, 5])print(tupla)print(tupla[2])

6.3.4 Dict, ionare

Daca vrem sa pastram asocieri simple de valori (ex nume student cu nota student, putem folosi dictionare. De exemplu:

studenti = {"Ion":10, "Maria":10, "Emil":10}studenti["Marcel"] = 9print(studenti["Emil"])

La materia Structuri de date s, i algoritmi vet,i învat,a despre timpii de acces la elemente s, i de avantajele dict,ionaruluica structura.

E de ment,ionat ca s, i aici elementele pot fi eterogene. Primul element din pereche (numit cheie) trebuie sa fie tipimutabil. Elementul al doilea (valoarea) poate fi orice.

6.3.5 Funct, ii, clase, obiecte

Putet,i trata funct,iile ca orice variabila normala. Putet,i crea liste, sa le trimitet,i ca parametri, apela din alte part,i aleprogramului, apela part,ial, etc. Dupa ce vet,i face Programare Funct,ionala, revenit,i. Altfel not,iunile va vor încurca.

Clasele (echivalentul struct din C) sunt s, i ele tipuri de date ce pot fi manipulate. La fel si obiectele.

6.4 Biblioteca matematica

Python-ul este scris în C. Este oarecum us, or pentru un program scris în Python sa interact,ioneze cu un cod compilatC. Multe biblioteci sunt scrise în limbajul C, profitând de viteza s, i avantajele unui cod executat direct de procesor darofera s, i "interfet,e" (bindings) spre Python.

Una dintre cele mai folosite în mediul de automatizari s, i în mediul s, tiint,ific (evident daca se foloses, te ecosistemulPython) este numpy.

Tipul de data fundamental este array-ul. Matrice ce cont,ine acelas, i tip de data, cu diverse dimensiuni s, i cu formarectangulara (fiecare rând, de exemplu, are aceeas, i dimensiune) Avem array cu o dimensiune: vector, cu douadimensiuni: matrice, etc.

Pe aceste matrici se pot aplica o imensitate mare de operat,ii matematice. Aceste operat,ii sunt implementate astfel încâtsa se execute rapid (atât ca s, i algoritmi ales, i cât s, i ca modul cum sunt transformat,i în cod mas, ina).

Haidet,i sa vedem un exemplu:

80 Chapter 6. Limbajul s, i mediul Python

Page 87: Programarea Calculatoarelor - utcluj.rousers.utcluj.ro/~visoft/teach/programareacalculatoarelor.pdfProgramarea Calculatoarelor, Versiune 0.32 1.2Desfas˘, urare 1.2.1Abstractizare

Programarea Calculatoarelor, Versiune 0.32

Listing 6.5: Declararea unei matrici s, i calculul inversei

import numpy as np

tab = np.array([[1, 4, 3], [5, 9, -1], [-3, 4, 2]])tab = np.linalg.inv(tab)print(tab)

Se va afis, a:

[[ 0.16296296 0.02962963 -0.22962963][-0.05185185 0.08148148 0.11851852][ 0.34814815 -0.11851852 -0.08148148]]

Pot face indexari puternice în numpy, de exemplu, pentru a accesa primul rând: tab[0, :]. Adica, ia doar rândul0 s, i da-mi toata coloana, nu doar un element (semnificat,ia lui :). La fel s, i pe coloane. Expresia tab[:, 1] estevectorul [4 9 4].

Uitat,i-va s, i peste alte doua biblioteci, pandas s, i matplotlib. Avet,i doua biblioteci puternice pentru lucru cu datetabulare s, i pentru afis, are grafica.

Este ok sa va apucat,i sa lucrat,i fara sa s, tit,i prea multe, cunos, tint,ele de programare dobândite pâna acum va vor permitesa ajunget,i la o solut,ie pe care, odata ce învat,at,i limbajul s, i biblioteca, o vet,i rafina (mai elegant, mai pythonic si maiales, cu timpi de execut,ie mai buni).

6.5 Setarea unui toolchain

Executabilul Python "traies, te" într-o structura de directori unde îs, i va cauta dependint,ele. Adica, bibliotecile.

Exista mai multe versiuni de Pyhon (la ora scrierii acestui text va recomand 3.8), exista mai multe versiuni de bibliotecis, i exista versiuni care nu se "împaca" între ele. Pentru ca un cod Python sa se poata executa, e nevoie ca toate acestedependint,e sa se împace frumos.

Pentru a gestiona tot acest haos (dependency hell) va recomand sa folosit,i Anaconda.

Instalat,i Anaconda pentru sistemul vostru de operare s, i lansat,i o consola conda.

Aici vet,i crea un "mediu de dezvoltare virtual" (virtual environment) pentru Python. Putet,i avea câte medii virtualedorit,i, fiecare are numele s, i locat,ia lui pe disc.

Pentru început, sa cream un mediu care sa cont,ina biblioteca matematica, cea pentru calcul tabular s, i grafica:

conda create -y --copy --name pc python=3.8 numpy pandas matplotlib pytest

Numele noului mediu este pc. Dupa cum spune s, i la sfârs, itul procesului de instalare, putet,i activa mediul tastând:

conda activate pi.

Acum putet,i lansa un interpretor Python s, i sa testat,i/jucat,i. Atent,ie, se iese cu funct,ia exit().

Pentru programe mai complexe de 3 linii va recomand un mediu de dezvoltare. Pycharm de la JetBrains.

Dupa descarcare s, i instalare, creat,i un nou proiect. Configurat,i proiectul sa se execute cu mediul dvs de Python. (Deobicei File -> Settings -> Project -> Python Interpreter)

Încercat,i sa scriet,i câteva linii de cod s, i sa rulat,i.

Dupa ce at,i setat mediul Python (cu Anaconda) s, i IDE-ul, NU mai e necesar sa deschidet,i o consola Anaconda. Putet,iporni direct PyCharm s, i coda acolo.

Succes!

6.5. Setarea unui toolchain 81

Page 88: Programarea Calculatoarelor - utcluj.rousers.utcluj.ro/~visoft/teach/programareacalculatoarelor.pdfProgramarea Calculatoarelor, Versiune 0.32 1.2Desfas˘, urare 1.2.1Abstractizare

Programarea Calculatoarelor, Versiune 0.32

To Do

for la python,

funcii cu return mutiplu si parametri cu valori default

egalitatea

82 Chapter 6. Limbajul s, i mediul Python

Page 89: Programarea Calculatoarelor - utcluj.rousers.utcluj.ro/~visoft/teach/programareacalculatoarelor.pdfProgramarea Calculatoarelor, Versiune 0.32 1.2Desfas˘, urare 1.2.1Abstractizare

CHAPTER 7

Limbajul s, i mediul MATLAB

MATLAB este un produs proprietar al firmei Mathworks. În cadrul universitat,ii beneficiat,i de licent, a pentrumajoritatea pachetelor.

Programul (softul, mediul de dezvoltare) este folosit mult în calcul numeric, electronica, simulari fizice mecanice,electrice, etc.

Vom atinge foarte pe scurt câteva elemente de sintaxa. Documentat,ia este extinsa. Sunt nenumarate surse pe internet,fiecare axata pe specificul ei. Vet,i afla mai multe când vet,i folosi Matlab la diverse materii.

Elementul central al limbajului este matricea. Ca sa creez o matrice, scriu cifre între paranteze drepte:

Listing 7.1: O matrice bidimensionala în Matlab. Introducet,i la consolaMatlab:

a = [1,2,3,4]

Va afis, a:

a =

1 2 3 4

Am declarat un vector.

Pentru o matrice 2D despart,im rândurile cu ;. A = [1 1 1; 2 2 2]

to do

Organizarea spat,iului de lucru, cu memoria de lucru (workspace-ul)

Operatii cu matrici (si cum scriu matrice 2D)

For, while

definitia de funcii si module, si cum le accesez sau returnez valori

Un exemplu cu ceva calcule poate

83

Page 90: Programarea Calculatoarelor - utcluj.rousers.utcluj.ro/~visoft/teach/programareacalculatoarelor.pdfProgramarea Calculatoarelor, Versiune 0.32 1.2Desfas˘, urare 1.2.1Abstractizare

Programarea Calculatoarelor, Versiune 0.32

84 Chapter 7. Limbajul s, i mediul MATLAB

Page 91: Programarea Calculatoarelor - utcluj.rousers.utcluj.ro/~visoft/teach/programareacalculatoarelor.pdfProgramarea Calculatoarelor, Versiune 0.32 1.2Desfas˘, urare 1.2.1Abstractizare

CHAPTER 8

Planificare curs

Atent,ie, planificarea este doar orientativa. Unele capitole, des, i fac parte din curs, s-ar putea sa nu fie listate.

Cursul 1

Introducere

Introducere

Un program simplu

Instruct,iuni de control

Cursul 2

Instruct,iuni, expresii s, i funct,ii

Memoria si variabilele

Intrari s, i ies, iri

Lean

Cursul 3

Introducere

Tipuri de memorie accesibile programului

Variabile (date) compuse

Cursul 4

Pointeri

Expresii

Cursul 5

Funct,ii

Intrari/ies, iri

Cursul 6

85

Page 92: Programarea Calculatoarelor - utcluj.rousers.utcluj.ro/~visoft/teach/programareacalculatoarelor.pdfProgramarea Calculatoarelor, Versiune 0.32 1.2Desfas˘, urare 1.2.1Abstractizare

Programarea Calculatoarelor, Versiune 0.32

Probleme propuse

Cursul 7

Toolchain-ul de compilare

Programarea modulara

Variabile statice s, i constante

Cursul 8

Lucrul cu date structurate

Cursul 9

Functii

Cursul 10

Strategii de programare

Cursul 11

Limbajul s, i mediul Python

Cursul 12

Limbajul s, i mediul MATLAB

Cursul 13

Recapitulare

Cursul 14

Pregatire pentru examen

86 Chapter 8. Planificare curs

Page 93: Programarea Calculatoarelor - utcluj.rousers.utcluj.ro/~visoft/teach/programareacalculatoarelor.pdfProgramarea Calculatoarelor, Versiune 0.32 1.2Desfas˘, urare 1.2.1Abstractizare

CHAPTER 9

Bibliografie

1. Ignat I., C.L. Ignat, "Programarea calculatoarelor – Descrierea algoritmilor s, i fundamentele limbajului C/C++",Ed. Albastra, Cluj-Napoca, 2005

2. Liviu Negrescu, "Limbajele C si C++ pentru începatori. Limbajul C", Editura Albastra, Cluj-Napoca, 1994

3. Ash Maurya, "Running Lean: Iterate from Plan A to a Plan That Works". Editura O'Reilly, diverse edit,ii

87


Recommended