Algoritmi Si Tehnici de Programare - Partea I - Material Suport

April 23, 2017 | Author: Bogdan Heim | Category: N/A
Share Embed Donate


Short Description

Algoritmi Si Tehnici de Programare - Partea I - Material Suport...

Description

Academia de Studii Economice din Bucureşti

Facultatea de Cibernetică, Statistică şi Informatică Economică Catedra de Informatică Economică

Cristian Uscatu

Cătălina Cocianu

Cătălin Silvestru

Algoritmi în programarea calculatoarelor Material didactic pentru ID

Acest material are la bază lucrarea Programarea calculatoarelor. Algoritmi în programare autori I. Gh. Roşca, B. Ghilic-Micu, C. Cocianu, M. Stoica, C. Uscatu, M. Mircea publicată la Editura ASE Bucureşti, 2007

Editura ASE Bucureşti 2010

Algoritmi în programare

2

Copyright © 2011, Cristian Uscatu, Cătălina Cocianu, Cătălin Silvestru Toate drepturile asupra acestei ediţii sunt rezervate autorului

Editura ASE Piaţa Romană nr. 6, sector 1, Bucureşti, România cod 010374 www.ase.ro www.editura.ase.ro [email protected]

Referenţi: Prof. univ. dr. Ion Gh. ROŞCA Prof. univ. dr. Gabriel ZAMFIR

ISBN 978-606-505-465-3

* Material didactic pentru ID *

Titlul cursului:

3

Algoritmi în programare

Introducere: Cursul Algoritmi în programare se adresează studenţilor facultăţii de Cibernetică, Statistică şi Informatică Economică din cadrul Academiei de Studii Economice din Bucureşti. Conform planului de învăţămînt, cursul se desfăşoară în semestrul 1 al anului 2 de studiu. Cursul Algoritmi în programare este destinat iniţierii studenţilor în cîteva aspecte ale programării calculatoarelor. Limbajul utilizat pentru demonstrarea conceptelor şi aplicaţiile practice este C standard (ANSI C). Obiectivele principale ale cursului vizează însuşirea de către studenţi a cunoştinţelor teoretice şi a abilităţilor practice de lucru referitoare la următoarelor elemente din cadrul programării calculatoarelor: tipurile dinamice de date şi modul de lucru cu date alocate dinamic (masive de date); analiza, proiectarea, implementarea şi utilizarea a subprogramelor; structurile de date externe (fişiere) şi abilităţi de lucru cu acestea. Cursul Algoritmi în programare este structurat în patru unităţi de învăţare, corespunzătoare elementelor principale studiate (date dinamice şi subprograme – cîte o unitate de învăţare, fişiere de date – 2 unităţi de învăţare). În cadrul procesului de instruire pot fi utilizate ca resurse suplimentare materialele puse la dispoziţie de biblioteca facultăţii, atît în cadrul sălii de lectură cît şi prin serviciul de împrumut. De asemenea, laboratoarele Catedrei de Informatică Economică pot fi utilizate pentru studiu individual pe toată durata programului de lucru, atunci cînd nu se desfăşoară ore (programarea orelor în laboratoare poate fi consultată la secretariatul Catedrei). Calculatoarele din laboratoare oferă medii de programare adecvate pentru dezvoltarea abilităţilor de lucru în limbajul C şi posibilitatea de accesare a bibliotecii virtuale a instituţiei. Evaluarea cunoştinţelor se va realiza prin intermediul lucrărilor susţinute pe parcursul semestrului astfel: o probă practică, constînd în rezolvarea a două probleme de programare din cadrul tematicii cursului; o lucrare scrisă. Ambele teste se susţin în timpul orelor aferente temelor de control din calendarul disciplinei. Cele două teste contribuie la formarea notei finale astfel: proba practică constituie 40% din nota finală; lucrarea scrisă constituie 50% din nota finală; din oficiu se acordă 10%.

Algoritmi în programare

4

Cuprins 1. Tipuri dinamice de date....................................................................................................... 6 Obiectivele unităţii de învăţare ............................................................................................... 6 1.1.

Tipuri de date dinamice............................................................................................... 6

1.2.

Declararea şi iniţializarea pointerilor ........................................................................ 7

1.3.

Utilizarea pointerilor.................................................................................................... 8 1.3.1. Operaţii cu pointeri................................................................................................ 8 1.3.2. Legătura între pointeri şi masive........................................................................ 10

1.4.

Alocarea dinamică a memoriei.................................................................................. 12

1.5.

Modificatorul const .................................................................................................... 13

1.6.

Tratarea parametrilor din linia de comandă........................................................... 14

Răspunsuri şi comentarii la testele de autoevaluare ........................................................... 14 Rezumat................................................................................................................................... 15 Bibliografia unităţii de învăţare............................................................................................ 15 2. Subprograme ...................................................................................................................... 16 Obiectivele unităţii de învăţare ............................................................................................. 16 2.1.

Construirea şi apelul subprogramelor ..................................................................... 16

2.2.

Transferul datelor între apelator şi apelat............................................................... 19 2.2.1. Transferul prin parametri................................................................................... 19 2.2.2. Simularea transmiterii parametrilor prin adresă ............................................. 21 2.2.3. Comunicaţia prin variabile globale .................................................................... 22

2.3.

Pointeri spre funcţii.................................................................................................... 23

2.4.

Funcţii cu număr variabil de parametri .................................................................. 27

2.5.

Calcul recursiv............................................................................................................ 30

2.6.

Exemple de aplicaţii cu subprograme recursive...................................................... 32

Răspunsuri şi comentarii la testele de autoevaluare ........................................................... 37 Rezumat................................................................................................................................... 39 Bibliografia unităţii de învăţare............................................................................................ 39

* Material didactic pentru ID *

5

3. Articolul şi fişierul .............................................................................................................. 40 Obiectivele unităţii de învăţare ............................................................................................. 40 3.1.

Articol: caracteristici generale şi mod de declarare................................................ 40

3.2.

Referirea articolului şi a elementelor componente.................................................. 42

3.3.

Articole cu structuri complexe .................................................................................. 45

3.4.

Constante de tip articol .............................................................................................. 46

3.5.

Fişierul şi articolul ...................................................................................................... 47

3.6.

Metode de organizare a fişierelor şi tipuri de acces ................................................ 48

3.7.

Structura sistemului de fişiere sub MS-DOS/Windows .......................................... 50

3.8.

Operaţii de prelucrare a fişierelor ............................................................................ 52 3.8.1. Nivelul inferior de prelucrare a fişierelor ........................................................ 53 3.8.2. Nivelul superior de prelucrare a fişierelor ....................................................... 56

Răspunsuri şi comentarii la testele de autoevaluare ........................................................... 63 Rezumat................................................................................................................................... 63 Bibliografia unităţii de învăţare ............................................................................................ 63 4. Algoritmi de prelucrare a fişierelor de date..................................................................... 64 Obiectivele unităţii de învăţare ............................................................................................. 64 4.1.

Caracteristici generale ale algoritmilor de prelucrare a fişierelor ........................ 64

4.2.

Algoritmi de prelucrare a fişierelor binare care nu necesită actualizare.............. 69

4.3.

Algoritmi de prelucrare a fişierelor binare care necesită actualizare ................... 77 4.3.1. Codificarea externă prin numere relative ........................................................ 77 4.3.2. Codificarea internă prin numere relative ........................................................ 79 4.3.3. Corespondenţa internă dintre chei şi numere relative.................................... 80

4.4.

Sortarea fişierelor binare memorate dens................................................................ 90

4.5.

Interclasarea fişierelor binare memorate dens ........................................................ 94

4.6.

Prelucrarea masivelor memorate în fişiere binare.................................................. 95 4.6.1. Prelucrarea vectorilor ........................................................................................ 95 4.6.2. Prelucrarea matricelor....................................................................................... 96

Răspunsuri şi comentarii la testele de autoevaluare ........................................................... 98 Bibliografia unităţii de învăţare ............................................................................................ 98 Bibliografie .............................................................................................................................. 98

Algoritmi în programare

6

1. Tipuri dinamice de date Cuprins Obiectivele unităţii de învăţare ............................................................................................... 6 1.1. Tipuri de date dinamice............................................................................................... 6 1.2. Declararea şi iniţializarea pointerilor ........................................................................ 7 1.3. Utilizarea pointerilor.................................................................................................... 8 1.3.1. Operaţii cu pointeri................................................................................................ 8 1.3.2. Legătura între pointeri şi masive........................................................................ 10 1.4. Alocarea dinamică a memoriei.................................................................................. 12 1.5. Modificatorul const .................................................................................................... 13 1.6. Tratarea parametrilor din linia de comandă........................................................... 14 Răspunsuri şi comentarii la testele de autoevaluare ........................................................... 14 Rezumat................................................................................................................................... 15 Bibliografia unităţii de învăţare............................................................................................ 15 Obiectivele unităţii de învăţare După studierea acestei unităţi de învăţare, studenţii vor avea cunoştinţe teoretice şi abilităţi practice despre tipurile dinamice de date utilizate în programarea calculatoarelor şi vor putea utiliza aceste tipuri de date şi structurile de tip masiv de date pentru rezolvarea problemelor de programare. Concret, se vor asimila cunoştinţe şi abilităţi de lucru privind: tipurile de date pointer; operaţii cu datele de tip pointer şi aritmetica pointerilor; legătura dintre pointeri şi masivele de date, în limbajul C; alocarea dinamică a datelor; tratarea parametrilor primiţi în linia de comandă. 1.1. Tipuri de date dinamice Pointerul este un tip de dată predefinit, care are ca valoare adresa unei zone de memorie (figura 1.1). Memoria internă Segmen:offset Pointer

Zona de memorie indicată de pointer

Fig. 1.1. Un pointer este adresa unei alte zone de memorie

* Material didactic pentru ID *

• • • •

* & *

7

Folosirea pointerilor prezintă următoarele avantaje: înlocuirea expresiilor cu indici – înmulţirile din formula de calcul al rangului se transformă în adunări şi deplasări; posibilitatea alocării dinamice a memoriei; folosirea tipurilor procedurale de date; calculul adreselor. În operaţiile cu pointeri se folosesc următorii operatori specifici: Operatori Simbol Utilizare * tip* Operator de referenţiere & &nume Operator de referenţiere * *nume Operator de dereferenţiere ⇒ defineşte un nou tip de dată (pointer la tip); ⇒ extrage adresa unei variabile (creează o referinţă); ⇒ accesează conţinutul zonei de memorie indicate de pointer. Cei doi operatori au efect invers: *&nume Ù

nume.

Exemplu 1. *&nume reprezintă valoarea de la adresa variabilei nume (valoarea variabilei nume). 1.2. Declararea şi iniţializarea pointerilor Fie TIP un tip de dată oarecare în limbajul C (inclusiv void). Declararea TIP* nume; este o declaraţie de pointer. TIP* este un nou tip de dată denumit pointer spre TIP, iar nume este o variabilă de tipul pointer spre TIP. Exemple 2. int* n;

⇒ n este o variabilă de tip pointer spre întreg; {a,b:real;}* x; ⇒ x este o variabilă de tip pointer spre o structură de ti-

3. struct complex pul complex; 4. void* p; ⇒ p este o variabilă de tip pointer spre void; p poate primi ca valoare adresa unei zone de memorie de orice tip. Dacă TIP este un tip oarecare (mai puţin void) atunci tipul TIP* este adresa unei zone de memorie de un tip cunoscut. Operaţiile care se pot efectua asupra zonei respective de memorie sînt definite de tipul acesteia. Dacă TIP este void, atunci TIP* este adresa unei zone de memorie de tip necunoscut. Deoarece nu se cunoaşte tipul zonei de memorie, nu sînt definite operaţiile care se pot efectua asupra ei. Pentru pointerii din exemplele anterioare se rezervă în memoria principală (în segmentul de date) cîte o zonă de 4B în care se va memora o adresă. Cînd variabila nume nu este iniţializată prin declarare, ea primeşte implicit valoarea NULL. La execuţie, poate primi ca valoare adresa unei variabile numai de tipul TIP. Dacă TIP este void, atunci nume poate primi adresa oricărei variabile, de orice tip.

Algoritmi în programare

Exemple 4. int*

8

nume; int a; float b;

nume = &a; nume = &b;

⇒ este o atribuire corectă; nume are ca valoare adresa variabilei a. ⇒ este o atribuire incorectă; nume poate primi ca valoare doar adresa

unei variabile întregi. 5. void* nume; int a; float b; nume = &a; nume = &b;

⇒ pointer fără tip ⇒ variabile de tip întreg, respectiv real ⇒ atribuire corectă ⇒ atribuire corectă; nume poate primi ca valoare adresa oricărei

variabile, de orice tip. Iniţializarea pointerilor se poate realiza ca în exemplul precedent sau, ca şi pentru celelalte variabile, la declarare, astfel: int a; int* nume=&a;

Se observă folosirea operatorului de referenţiere & pentru a crea o referinţă către variabila a. La alocarea dinamică a memoriei se foloseşte o altă metodă pentru iniţializarea unui pointer. Operatorul de dereferenţiere se utilizează atît pentru definirea tipului pointer, cît şi pentru referirea datelor de la adresa indicată de pointer. Exemplu 6. int a,b,c; int* nume; void* nume2; b=5; nume=&a; *nume=b; c=*nume+b; nume2=&b; *(int*)nume2=10; c=*(int*)nume2;

Se observă folosirea conversiei de tip (typecasting), atunci cînd se lucrează cu pointeri spre tipul void (fără tip). Chiar dacă un pointer spre tipul void poate primi ca valoare adresa unei variabile de orice tip, pentru a putea lucra cu ea este necesară gestionarea corectă a tipului operanzilor. Teste de autoevaluare 1. Care sînt operatorii specifici lucrului cu pointeri în limbajul C? Daţi exemple de utilizare a lor. 2. Care sînt cele două tipuri de pointeri? Care sînt diferenţele dintre ele? 1.3. Utilizarea pointerilor 1.3.1. Operaţii cu pointeri Asupra pointerilor se pot efectua operaţii aritmetice. Fie secvenţa: int *nume,*nume2, c, a, b; nume=&a; nume2=&a;

* Material didactic pentru ID *

9

Incrementare/decrementare Dacă nume este pointer spre un tip TIP, prin incrementare/decrementare, valoarea lui nume se incrementează/decrementează cu numărul de octeţi necesari pentru a memora o dată de tip TIP, adică cu sizeof(TIP). nume++ Ù nume are ca valoare o adresă care este incrementată şi primeşte valoarea nume+sizeof(int) (care este adresa lui b); nume2-Ù nume are ca valoare o adresă care este decrementată şi primeşte valoarea nume-sizeof(int) (care este adresa lui c); Situaţia iniţială este următoarea:

nume

nume2

4B

c

4B

a 2B 2B

b 2B

După cele două operaţii: nume

nume2

4B

4B

c

a 2B 2B

b 2B

Analog se execută operaţiile ++nume şi --nume. Exemplu 7. float v[20]; float* p; int i; p=&v[i];

unde i poate avea valori între 0 şi 19

În urma atribuirii ++p sau lui v[i+1].

p++,

p va avea ca valoare adresa lui v[i] plus 4 octeţi, adică adresa

Adunarea/scăderea unui întreg În general, dacă p este un pointer spre un tip TIP, atunci cînd se adună un întreg n la pointerul p, rezultatul va fi tot un pointer spre TIP, care are ca valoare adresa memorată în p, la care se adună de n ori numărul de octeţi necesari pentru a memora o dată de tip TIP, adică n*sizeof(TIP). Asemănător se execută scăderea unui întreg dintr-un pointer. nume+n Ù nume primeşte valoarea nume+n*sizeof(int) nume-n Ù nume primeşte valoarea nume-n*sizeof(int) Exemplu 8. Fie p şi q pointeri spre tipul float (float* p, *q). Presupunînd că p a fost iniţializat cu valoarea 0x0fff:0x3450, în urma operaţiei q=p+3, q primeşte valoarea 0xfff:0x345c (se adună 3*4 octeţi). În urma operaţiei q=p-2, q primeşte valoarea 0xffff:0x344a (se scad 2*4 octeţi). Operaţiile descrise anterior se folosesc frecvent în lucrul cu masive. Compararea a doi pointeri Limbajul C permite compararea a doi pointeri într-o expresie, folosind oricare din operatorii relaţionali (==, !=, , =). Rezultatul expresiei nume op nume2 (unde op este unul din operatorii precizaţi anterior) este adevărat (nenul) sau fals (zero) după cum nume este egal, mai mare sau mai mic decît nume2. Doi pointeri sînt egali dacă adresele care constituie valorile lor sînt egale. Privind memoria internă liniar, începînd de la adresa 0 (zero), un pointer p este mai mare decît altul q, dacă adresa pe care o conţine p este mai îndepărtată de începutul memoriei decît adresa conţinută de q.

Algoritmi în programare

10

Este permisă şi compararea unui pointer cu o valoare constantă. Uzual se foloseşte comparaţia cu valoarea NULL pentru a verifica dacă pointerul a fost iniţializat (un pointer neiniţializat are valoarea NULL), folosind unul din operatorii == sau !=. Valoarea NULL este definită în stdio.h astfel: #define NULL 0 De multe ori se preferă comparaţia directă cu zero (nume==0 sau nume!=0). În loc de nume==0 se poate folosi expresia nume. Aceasta se interpretează astfel: dacă nume nu a fost iniţializat, atunci are valoarea NULL (adică 0), deci expresia este falsă. În caz contrar valoarea expresiei este nenulă, deci adevărată. Asemănător se foloseşte expresia !nume. Exemplu 9. float* p,q,r,t; float a,b; p=&a; q=&b; r=&a; a=5; b=7; if(t) printf("Pointer initializat!\n"); else printf("Pointer neinitializat!\n"); if(p==r) printf("Pointeri egali\n"); else printf("Pointeri diferiti\n"); if(p>q) printf("%d\n",a); else printf("%d\n",b);

Pe ecran se va afişa: Pointer neinitializat! Pointeri egali 7

deoarece t are valoarea NULL, variabilele p şi r au ca valoare adresa lui a, iar q conţine adresa lui b, care este mai mare decît a lui a (datorită faptului că a a fost alocat primul). Diferenţa dintre doi pointeri Fie secvenţa: int m[50],* a, * b; a=&m[i]; b=&m[j];

unde i şi j sînt întregi în intervalul [0..49]. Expresia a-b are valoarea i-j, interpretată ca distanţă între adresele a şi b, exprimată în zone de memorie de lungime sizeof(int). Valoarea unei expresii diferenţă se calculează astfel: se face diferenţa între cele două adrese (în octeţi), apoi se împarte la dimensiunea tipului de dată referită de cei doi pointeri (tipul int în exemplul de mai sus – vezi figura 1.2). Cei doi pointeri trebuie să refere acelaşi tip de dată, altfel rezultatul nu are semnificaţie (trebuie să aibă tipuri identice). Operaţia este utilă în lucrul cu masive. m

i

a

j

b

Fig.1.2. Reprezentarea semnificaţiei variabilelor din exemplul anterior

Atenţie: vorbim despre diferenţa dintre doi pointeri (înţelegînd distanţa dintre cele două adrese), nu despre scăderea a doi pointeri. 1.3.2. Legătura între pointeri şi masive În limbajul C numele unui masiv este un pointer către tipul de dată al elementele masivului.

* Material didactic pentru ID *

11

Pentru masivele unidimensionale: int m[50]; Ù m are tipul int* int* p; Ù p are tipul int* Diferenţa constă în faptul că zona de memorie către care punctează m este rezervată la compilare (ceea ce nu se întîmplă în cazul pointerilor declaraţi ca atare). De aceea m nici nu poate primi valori în timpul execuţiei programului (nu se poate schimba adresa memorată în m). El memorează adresa primului element din masiv. Referirea unui element m[i] este echivalentă cu *(m+i) – conţinutul de la adresa m+i. Limbajul C nu face nici un fel de verificări în privinţa depăşirii limitelor indicilor masivului, de aceea expresiile m[500] sau m[-7] vor fi considerate corecte de compilator, existînd riscul unor erori logice. Este sarcina programatorului să se asigure că indicii nu vor depăşi limitele. Pentru masivele bidimensionale: int m[50][50]; Ù m are semnificaţia următoare: m[i][j] Ù *(*(m+i)+j), reprezintă „conţinutul de la adresa j plus conţinutul de la adresa memorată în m plus i”. Aceasta poate fi interpretată astfel: m este un pointer spre un vector de pointeri, fiecare element al vectorului fiind la rîndul lui un pointer spre o linie a matricei (un vector de elemente de tip float). Figura 1.3. poate fi utilizată pentru a înţelege mai bine cum se accesează elementele unui masiv bidimensional. Atenţie: această figură NU reprezintă modul de alocare în memorie a unei matrice statice! Doar pentru matricele alocate dinamic zonele de memorie sînt alocate în acest fel. m

m[0,0]

m[0,1]



m[0,49]

m[0,0]

m[0,1]



m[0,49]

m[2,0]

m[2,1]



m[2,49]

m[3,0]

m[3,1]



m[3,49]

m[4]

m[4,0]

m[4,1]



m[4,49]

… m[49]











m[49,49

m[0] m[1] m[2] m[3]

m[49,0] m[49,1]

Fig.1.3. Reprezentarea modului de alocare dinamică a spaţiului necesar pentru memorarea unei matrice 50x50

Analog pot fi interpretate masivele cu mai multe dimensiuni. Exemplu 10. Un masiv cu trei dimensiuni float m[10][10][10] poate fi interpretat ca un pointer spre un vector de pointeri spre matrice; Un masiv cu n dimensiuni este tratat ca un pointer spre un vector de pointeri către masive cu n-1 dimensiuni. Pentru a lucra cu elementele unui masiv static se poate folosi adresarea indexată (m[i] pentru vectori sau m[i][j] pentru matrice) sau adresarea elementelor prin pointeri (*(m+i) pentru vectori sau *(*(m+i)+j) pentru matrice etc). Mai mult, pentru vectori se poate declara un pointer iniţializat cu adresa de început a masivului, iar elementele masivului să fie referite prin intermediul acestui pointer. Exemplu 11. float*

v[10];

float* p; p=v;

Algoritmi în programare

12

După atribuire, pointerul p conţine adresa de început a masivului şi poate fi folosit pentru referirea elementelor masivului. De exemplu, v[3] şi p[3] referă aceeaşi zonă de memorie. Test de autoevaluare 3. Să se scrie secvenţa de program care citeşte de la tastatură elementele unei matrice, folosind expresii cu pointeri pentru adresarea elementelor matricei. 1.4. Alocarea dinamică a memoriei Pentru a memora o dată de un anumit tip în heap este necesar să se declare un pointer către acel tip de dată, apoi să se rezerve memoria necesară. Pentru a rezerva spaţiu în heap se foloseşte funcţia standard: void* malloc(unsigned n);

Funcţia rezervă o zonă de n octeţi în heap şi returnează adresa acesteia. Deoarece funcţia returnează pointer spre void este necesară conversia rezultatului spre tipul dorit, astfel: int* nume; nume=(int *) malloc(sizeof(int));

⇔ rezervă în heap spaţiu pentru o valoare de tip întreg.

Eliberarea unei zone de memorie rezervate anterior se face prin funcţia standard: void free(void* p);

Funcţia primeşte ca parametru un pointer (indiferent de tip) spre zona de memorie pe care trebuie să o elibereze. Limbajul C oferă posibilitatea de a aloca contiguu zone de memorie pentru mai multe date de acelaşi tip, prin funcţia standard: void* calloc(unsigned nr_elem, unsigned dim_elem);

Funcţia calloc rezervă o zonă contiguă de memorie pentru mai multe elemente de acelaşi tip, întorcînd un pointer spre zona respectivă. Există şi o variantă a lui malloc care returnează în mod explicit un pointer „îndepărtat” (far): void* farmalloc(unsigned long n);

Pentru eliberarea unei zone de memorie rezervate prin farmalloc se foloseşte funcţia standard: void farfree(void* p);

Exemple 12. int* masiv; masiv=(int*)calloc(50,sizeof(int)); memorie pentru un vector cu 50 de elemente întregi.

⇔ rezervă spaţiu de

13. Alocarea de spaţiu în heap pentru o matrice se face conform figurii 1.3 pentru a putea accesa elementele la fel ca în cazul unei matrice statice, prin dublă indexare; int** m; int n,p; /* se alocă spaţiu pentru vectorul cu adresele celor n linii ale matricei */ m=(int**)malloc(m*sizeof(int*)); for(int i=0;i
View more...

Comments

Copyright ©2017 KUPDF Inc.
SUPPORT KUPDF