Differences

This shows you the differences between two versions of the page.

Link to this comparison view

programare:laboratoare:lab05 [2020/11/05 10:46]
oana.balan [Referinţe]
programare:laboratoare:lab05 [2025/10/15 18:30] (current)
darius.neatu [Studiu de caz]
Line 1: Line 1:
-===== TablouriParticularizare - vectori. Aplicaţii ​=====+===== PCLP Laborator05:​ PointeriAbordarea lucrului cu tablouri folosind pointeri  ​=====
  
 **Responsabili:​** **Responsabili:​**
-  * [[neatudarius@gmail.com|Darius Neațu (CA 2019-2020)]] +  * [[neatudarius@gmail.com|Darius Neațu (2019-2020)]] 
-  * [[ion_dorinel.filip@cti.pub.ro|Dorinel Filip (CA 2019-2020)]] +  * [[ion_dorinel.filip@cti.pub.ro|Dorinel Filip (2019-2020)]] 
-  * [[andrei.parvu@cti.pub.ro|Andrei Pârvu]] +  * [[laura.vasilescu@cti.pub.ro|Laura Vasilescu]]
  
-**Teste:** [[https://​drive.google.com/​file/​d/​11NU3wwFm7CAlkjqNBpWrYDTWqoRqEoqe/​view?​usp=sharing|Link]] + 
-==== Obiective ====+===== Obiective ​=====
  
 În urma parcurgerii acestui laborator, studentul va fi capabil: În urma parcurgerii acestui laborator, studentul va fi capabil:
 +  *  să înţeleagă noţiunea de pointer şi modurile în care se poate opera cu memoria în limbajul C
 +  *  să cunoască modul în care pointerii sunt folosiţi în a returna sau modifica parametri în cadrul unei funcţii
 +  *  să înţeleagă noţiunea de pointer la o funcţie şi să-l folosească în situaţiile în care acesta este necesar;
 +  *  să folosească funcţiile de alocare şi de eliberare a memoriei
  
-  * să declare şi să iniţializeze vectori (din declaraţie şi prin structuri iterative) +{{ http://​imgs.xkcd.com/​comics/​pointers.png}}
-  * să implementeze algoritmi simpli de sortare şi căutare pe vectori +
-  * să folosească practici recunoscute şi recomandate pentru scrierea de cod sursă care implică lucrul cu vectori +
-  * să recunoască şi să evite erorile comune de programare legate de aceste structuri +
-  * să aplice lucrul cu vectori pentru rezolvarea unor probleme cu dificultate medie+
  
-==== Noţiuni teoretice ==== 
  
-=== Vectori ​===+===== Noţiunea de pointer =====
  
-Printr-un vector se înţelege ''​o colecţie liniară şi omogenă''​ de date. Un vector ​este liniar pentru că datele(elementele) pot fi accesate în mod unic printr-un ''​index''​. Un vector este, de asemenea, omogen, pentru că toate elementele sunt de acelaşi ''​tip''​. În limbajul C, indexul este un număr întreg pozitiv şi indexarea se face începând cu 0+Un **pointer** ​este o variabilă care reţine o adresă de memorie.
  
-Declaraţia unei variabile ​de tip vector se face în felul următor:+În C, un pointer poate reprezenta:​ 
 +  - adresa unor date de un anumit tip  
 +    * tip elementar, structură, şir de caractere etc. 
 +    * operațiile cu pointeri sunt determinate de dimensiunea tipului de date 
 +  - adresa unei funcții 
 +    * adresa la care punctul curent de execuţie va sări, în cazul în care acea funcţie este apelată 
 +  - adresa ​unei adrese ​de memorie 
 +    * acest tip de pointer poate fi redus la prima situaţie 
 +  - adresa unei zone cu conţinut necunoscut (pointer către void)
  
-<code bash+<note tip
-<​tip_elemente>​ <​nume_vector>​[<​dimensiune>​];​ +Dimensiunea unui pointer depinde de arhitectura și sistemul de operare pe care a fost compilat programul. Dimensiunea unui pointer se determină cu **''​sizeof(void *)''​** și nu este în mod necesar egală cu dimensiunea unui tip de date întreg.
-</​code>​+
  
-De exemplu, avem următoarele declaraţii ​de vectori:+În cadrul laboratorului și al temelor de casă se vor utiliza mașini și pointeri pe 32 de biți. 
 +</​note>​
  
-<code c> +===== Operatori =====
-int a[100]; +
-float vect[50];+
  
-#define MAX 100 +==== Operatorul de referențiere ====
-... +
-unsigned long numbers[MAX] +
-</​code>​+
  
-Este de remarcat că vectorul este o ''​structură statică''​: dimensiunea acestuia trebuie să fie o constantă la compilare şi nu poate fi modificată ​în cursul execuţiei programului. Astfel, programatorul trebuie să estimeze o dimensiune maximă pentru vector, şi aceasta va fi o limitare ​programului. De obicei, se folosesc constante simbolice (ca în ultimul exemplu) pentru aceste dimensiuni maxime, pentru ca ele să poată fi ajustate uşor la nevoie.  +**''​&''​** - apare în fațvariabilei asupra căreia acționează
-De asemenea, în cadrul unei declaraţii,​ se pot iniţializa cu valori constante componente ale vectorului, iar în acest caz, dimensiunea vectorului poate rămâne neprecizată (compilatorul o va determina din numărul elementelor din listă). De exemplu:+
  
-<code c> +Este aplicat unei **variabileavand ORICE tip de date,** și **obține ADRESA de (din) memorie** a variabilei respective.
-int a[3] = {156};                     // Toate cele 3 elemente sunt initializate  +
-float num[] = {1.5, 2.3, 0.2, -1.3}; ​     // Compilatorul determina dimensiunea - 4 - a vectorului +
-unsigned short vect[1000] = {0, 2, 4, 6}; // Sunt initializate doar primele 4 elemente +
-</​code>​+
  
-În cazul special în care specificăm dimensiunea şi doar un singur element la initializare,​ primul element va fi cel specificat, iar toate celelalte elemente ale vectorului vor fi iniţializate la ''​0'':​+==== Operatorul de dereferențiere ====
  
-<code c> +**''​*''​** - apare în fața variabilei asupra căreia acționează
-char string[100] = {97}; // Sirul va fi initializat cu: 97 (caracterul ​'a') pe prima poziţie şi 99 de 0 +
-int v[100] = {0};        // Vectorul va fi umplut cu 0. +
-</​code>​+
  
-Este important de remarcat faptul că elementele ​**neiniţializate** pot avea valori ​**oarecare**. La alocarea unui vector, compilatorul nu efectuează nici un fel de iniţializare şi nu furnizează nici un mesaj de eroare dacă un element este folosit înainte de a fi iniţializat. ​**Un program corect va iniţializa**,​ în orice caz, fiecare element înainte de a-l folosi. +Este aplicat unei **variabile de tip pointer** și **obține valoarea stocată** la adresa respectivă (indicata ​de pointer).
-Elementele se accesează prin expresii ​de forma ''<​nume_vector>​[<​indice>​]''​De exemplu, putem avea:+
  
-<code c+<note warning
-char vect[100]; +Declararea unui pointer nu înseamnă alocarea unei zone de memorie în care pot fi stocate date. Un pointer este tot un tip de date, a cărui valoare este un număr ce reprezintă o adresă de memorie. 
-vect[0] = 1; +</​note>​
-vect[5] = 10;+
  
-int i = 90; +Pentru ca dereferențierea să aibă loc cu succes, pointer-ul trebuie să indice o adresă de memorie validă, la care programul are acces. Această adresă poate fi adresa unei variabile declarate în prealabil sau adresa unui bloc de memorie alocat dinamic (după cum vom vedea mai departe).
-vect[i] = 15; +
-vect[i + 1] = 20; +
-</​code>​+
  
-==== Stil de programare ====+Este indicată inițializarea pointerilor cu constanta [[http://​www.cplusplus.com/​reference/​cstddef/​NULL/​ | NULL]], compatibilă cu orice tip de pointer, care indica, prin convenție, un pointer neinițializat.
  
-=== Exemple de programe === 
- 
-Citirea unui vector de intregi de la tastatura: 
  
 +==== Particularități ====
 <code c> <code c>
-int main()  +int *a;             /* Pointer */ 
-{ +int b = 5         /* Variabila */ 
-  int a[100], n, i; /* vectorul a are maxim 100 de intregi ​*/+char *c;            /* Pointer catre un caracter (sau sir de caractere) */ 
 +void *buff = NULL;  /* Pointer catre void, initializat la NULL */
  
-  scanf("​%d",​ &​n); ​/* citeste nr de elemente vector ​*/+/* Asignare NEVALIDA; a este un pointer neinitializat ​*/ 
 +*a = 1; 
  
-  for (i = 0; i < n; i++) { +/* a ia adresa variabilei b */       
-    scanf("​%d",​ &​a[i]); ​/* citire elemente vector ​*/ +a = &b;
-  }+
  
-  for (i = 0; i < n; i++{ +/* Continutul memoriei de la adresa a (care a fost initializata mai sus)  
-    ​printf("%d ", a[i]); /* scrie elemente vector ​*/ +   ia valoarea 5. Acest lucru este echivalent cu "b = 5;" ​ 
-  } + */ 
-   +*a = 5  
-  return 0; +
-}+
 </​code>​ </​code>​
  
-Generarea unui vector cu primele n numere Fibonacci: +În cazul declaraţiilor de pointeri, operatorul ''​*''​este asociat numelui variabilei, şi nu numelui tipului, astfel că, pentru o declaraţie de mai multe variabile, operatorul ''​*''​ trebuie să apară pentru fiecare variabilă în parte şi este recomandat ca şi formatarea codului să indice această asociere. De exemplu:
 <code c> <code c>
-#include <​stdio.h>​ +/* sir1 e pointer, sir2 si sir3 sunt caractere */ 
-int main()  +char *sir1sir2sir3
-+
-  long fib[100] = {11}; +
-  int ni;+
  
-  printf("​n = ");  +/* ab si c sunt pointeri */ 
-  scanf("​%d"​&n); +int *a*b, *c;
-   +
-  for (i = 2; i < n; i++) { +
-    fib[i] = fib[i - 1] + fib[i - 2]; +
-  } +
-  for (i = 0; i < n; i++) { +
-    printf("​%ld "fib[i]); +
-  }+
  
-  return 0+/* Doar a este pointerformatarea codului este nerecomandata */ 
-}+char* a, b; 
 +</​code>​ 
 + 
 +Operatorul ''​*''​ poate fi folosit şi în specificarea numelui unui tip (de exemplu în cazul unui ''​cast''​),​ şi în acest caz el apare după numele tipului. De exemplu: 
 +<code c> 
 +void *var = NULL; 
 +int *num = (int *)var; // Operatie valida, dar riscanta
 </​code>​ </​code>​
  
 <note warning> <note warning>
-Deşi modul în care se manifestă erorile din program ​poate fi surprinzător şi imprevizibilcauzele care produc aceste erori sunt destul de comune şi pot fi grupate în mai multe categorii. Câteva dintre acestea sunt prezentate mai jos +Un pointer către ''​void''​ nu poate fi folosit direct în operaţii cu pointerici trebuie convertit ​mai întâi ​la un pointer către un tip de date
-</​note>​ +
- +
-  * Depăşirea limitelor indicilor (index out of bounds) este o eroare frecventă, ce poate duce la blocarea programului sau a sistemului şi poate fi evitată prin verificarea încadrării în intervalul valid.  +
-  * Indici folosiţi greşit în bucle imbricate (index cross-talk). Sunt multe cazuri în care pe un nivel al buclei se foloseşte, ​de exemplu vect[i], şi pe nivelul imbricat vect[j], când de fapt se dorea folosirea lui i. Mare atenţie şi în astfel de cazuri! +
- +
-<note tip> +
-Definiţi dimensiunile prin constante şi folosiţi-le pe acestea în locul tastării explicite a valorilor în codul sursă. Astfel veţi evita neconcordanţe în cod dacă doriţi ulterior să modificaţi dimensiunile şi uitaţi să modificaţi peste tot prin cod.+
 </​note>​ </​note>​
 +De exemplu:
 +<code c>
 +void *mem;
 +//[...]
 +*mem = 10;        // Operatie ILEGALA
 +((int *)mem) = 10; // Operatie legala, dar riscanta
 +</​code>​
 +===== Tipuri de pointeri =====
 + 
 +==== Pointeri la date ====
  
 +== Stocarea datelor la o anumită adresă; Citirea datelor de la o anumită adresă ==
 <code c> <code c>
-#define MAX   100+*p = y;         // Ia valoarea y si pune-o la adresa indicata de p 
 +x = *p;         // Ia valoarea de la adresa indicata de p si pune-o in variabila x 
 +*s1++ = *s2++;  
 +</​code>​
  
-int vect[MAX];+== Atribuirea unei adrese unui pointer ==  
 +<code c> 
 +p1 = p2; 
 +p = NULL; 
 +p = malloc(n); // Veti studia malloc() intr-un laborator ulterior
 </​code>​ </​code>​
-va fi de preferat în locul lui+ 
 +== Interpretarea diferită a datelor din memorie ==
 <code c> <code c>
-int vect[100];+int n; 
 +short s1, s2; 
 +s1 = *((short*)&​n); ​    // Extrage primul cuvant din intregul n 
 +s2 = *((short*)&​n + 1); // Extrage cel de-al doilea cuvant din intregul n
 </​code>​ </​code>​
  
-<note tip> +== Aritmetică cu pointeri == 
-Verificaţi că indicii se încadrează între marginile superioară şi inferioară a intervalului de valori valideAcest lucru trebuie în general făcut în cazul în care datele provin dintr-o sursă externă: citite ​de la tastatură sau pasate ca parametri efectivi unei funcţii, de exemplu+Adunarea sau scăderea unui întreg la un pointer, incrementarea sau decrementarea unui pointerAceste operaţii lucrează în multipli ​de dimensiunea tipului de date la care pointerii se referă, pentru a permite accesul la memorie ca într-un vector (a se vedea laboratorul ​de [[:​programare:​laboratoare:​lab05|tablouri]])De exemplu: ​ 
-</note>+<code c> 
 +int *num;
  
-Exemplu:+/* Aduna la adresa initiala dimensiunea tipului de date referit  
 +   de pointer (pe sizeof(int)),​ dand acces
 la urmatorul intreg 
 +   care ar fi stocat daca zona aceea de memorie ar fi organizata
 
 +   sub forma unui vector 
 +  */ 
 +num++;
  
 +/* Incrementeaza adresa cu 5 * sizeof(int) */
 +num = num + 5; 
 +</​code>​
 +==== Pointeri la tablouri ====
 +
 +După cum a fost prezentat în [[programare:​laboratoare:​lab05|laboratorul de vectori]], o variabilă vector conţine adresa de început a vectorului (adresa primei componente a vectorului),​ şi de aceea este echivalentă cu un pointer la tipul elementelor din vector. Această echivalenţă este exploatată,​ de obicei, în argumentele de tip vector şi în lucrul cu vectori alocaţi dinamic. De exemplu, pentru declararea unei funcţii care primeşte un vector de întregi şi dimensiunea lui, avem două posibilităţi:​
 <code c> <code c>
-// program care citeşte un index şi o valoareşi atribuie valoarea elementului din vector care se găseşte la poziţia respectivă +void printVec(int a[]int n); 
-#​include ​<stdio.h+</code
-#define N 10+sau 
 +<code c> 
 +void printVec(int *a, int n); 
 +</​code>​
  
-int main() +În interiorul funcţiei ne putem referi la elementele vectorului ''​a''​ fie prin indici, fie prin indirectare,​ indiferent de felul cum a fost declarat parametrul vector ''​a'':​ 
 +<code c> 
 +void printVec ​(int a[], int n
 { {
-  int i, val+  int i; 
-  ​int v[N]; +  ​for (i = 0i < n; i++
- +    printf("​%6d", ​a[i]); // Indexare
-  scanf("​%d%d",​ &i, &​val);​ +
- +
-  /* !!! Verific daca indexul este valid */ +
-  if (i >= 0 && ​i < N+
-   v[i] = val; +
-  } else { +
-    printf("​Introduceti un index >= 0 si < %d\n", ​N); +
-  } +
- +
-  return 0;+
 } }
 </​code>​ </​code>​
- +sau
-<note tip> +
-Folosiţi comentarii pentru a explica ce reprezintă diverse variabile. Acest lucru vă va ajuta atât pe voi să nu încurcaţi indici, de exemplu, cât şi pe ceilalţi care folosesc ​sau extind codul vostru. +
-</​note>​ +
- +
-Exemplu: +
 <code c> <code c>
-#include <​stdio.h>​ +void printVec ​(int *a, int n
-#define N 100 +
- +
-int main() +
 { {
-  int v[N]+  int i
-  ​int i, j/* indecsii elementelor ce vor fi interschimbate */ +  ​for (= 0i < ni++) 
-  int aux /* variabila ajutatoare pentru interschimbare */ +    ​printf("​%6d", ​*a++); // Indirectare
- +
-  /*... initializari ​*/ +
-   +
-  ​/* Interschimb */ +
-  aux = v[i]; +
-  v[i] = v[j]; +
-  v[j] = aux; +
- +
-  return 0;+
 } }
 </​code>​ </​code>​
  
-==== Aplicaţii cu vectori ​====+Astfel, există următoarele echivalenţe de notaţii pentru un vector ''​a'':​ 
 +<code c> 
 +a[0] <==> *a 
 +a[1] <==> *(a + 1) 
 +a[k] <==> *(a + k) 
 +&a[0] <==> a 
 +&a[1] <==> a + 1 
 +&a[k] <==> a + k 
 +</​code>​
  
-=== utări ===+Diferenţa dintre o variabilă pointer şi un nume de vector este aceea că un nume de vector este un pointer constant (adresa sa este alocată de către compilatorul ​şi nu mai poate fi modificată la execuţie), deci nu poate apărea în stânga unei atribuiri, în timp ce o variabilă pointer are un conţinut modificabil prin atribuire sau prin operaţii aritmetice. De exemplu: 
 +<code c> 
 +int a[100], *p; 
 +a; ++p; //corect 
 +p; ++a; //EROARE 
 +</​code>​ 
 +De asemenea, o variabilă de tip vector conţine şi informaţii legate de lungimea vectorului şi dimensiunea totală ocupată în memorie, în timp ce un pointer doar descrie o poziţie în memorie (e o valoarea punctuală). Operatorul ''​sizeof(v)''​ pentru un vector ''​v[N]''​ de tipul ''​T''​ va fi ''​N * sizeof(T)'',​ în timp ce ''​sizeof(v)''​ pentru o variabila ''​v''​ de tipul ''​T *''​ va fi ''​sizeof(T *)'',​ adică dimensiunea unui pointer.
  
-== Căutare secvenţială ==+Ca o ultimă notă, este importat de remarcat că o funcţie poate avea ca rezultat un pointer, dar nu poate avea ca rezultat un vector.
  
-Când avem de a face cu un vector nesortat (şi nu numai în acest caz), cea mai simplă abordare pentru a găsi o valoare, este căutarea secvenţială. Cu alte cuvinte, se compară, la rând, fiecare valoare din vector cu valoarea căutată. Dacă valoarea a fost găsită, căutarea se poate opri (nu mai are sens să parcugem vectorul până la capăt, dacă nu se cere acest lucru explicit). ​+==== Pointeri ​în funcţii ====
  
-''​Exemplu''​:+În cadrul funcţiilor,​ pointerii pot fi folosiţi, printre altele, pentru: 
 +  * Transmiterea de rezultate prin argumente  
 +  * Transmiterea unei adrese prin rezultatul funcţiei  
 +  * Utilizarea unor funcţii cu nume diferite (date prin adresele acestora) 
 +  
 +O funcţie care trebuie să modifice mai multe valori primite prin argumente sau care trebuie să transmită mai multe rezultate calculate în cadrul funcţiei trebuie să folosească argumente de tip pointer. ​
  
 +De exemplu, o funcţie care primeşte ca parametru un număr, pe care il modifica:
 <code c> <code c>
 +// Functie care incrementeaza un intreg n modulo m
 +int incmod (int *n, int m) {
 +  return ++(*n) % m;
 +}
  
-#define MAX 100+// Utilizarea functiei 
 +int main() { 
 +  int n = 10; 
 +  int m = 15; 
 +   
 +  incmod(&​n,​ m);   
 +  // Afisam noua valoare a lui n 
 +  printf("​n:​ %d", n); 
 +   
 +  return 0; 
 +</​code>​
  
-...+O funcţie care trebuie să modifice două sau mai multe argumente, le va specifica pe acestea individual, prin câte un pointer, sau într-un mod unificat, printr-un vector, ca în exemplul următor: 
 +<code c> 
 +void inctime (int *h, int *m, int *s); 
 +// sau 
 +void inctime (int t[3]); // t[0]=h, t[1]=m, t[2]=s 
 +</​code>​
  
-int v[MAX]xi; +O funcţie poate avea ca rezultat un pointerdar acest pointer nu trebuie să conţină adresa unei variabile locale. De obiceirezultatul pointer este egal cu unul din argumente, eventual modificat în funcţie. De exemplu: 
- +<code c> 
-/* initializari *+// Incrementare pointer p 
-... +char *incptr(char *p) { 
- +  ​return ++p;
-int found = 0; +
-for (i = 0; i < MAX; i++) { +
-  ​if (x == v[i]) { +
-    found = 1; +
-    break; +
-  } +
-+
- +
-if (found) { +
-   ​printf("​Valoarea %d a fost gasita in vector\n",​ x); +
-} else {  +
-   ​printf("​Valoarea %d nu a fost gasita in vector\n",​ x);+
 } }
-... 
 </​code>​ </​code>​
  
-== Căutare binară iterativă == +O variabila locală are o existenţă temporară, garantată numai pe durata execuţiei funcţiei în care este definită (cu excepţia variabilelor locale statice), şi de aceea adresa unei astfel ​de variabile nu trebuie transmisă în afara funcţieipentru a fi folosită ulteriorDe exempluurmătoarea secvenţă de cod este ''​greşită''​:
- +
-Dacă vectorul ​pe care se face căutarea ​este sortat, algoritmul mai eficient de folosit în acest caz este căutarea binară. Presupunem că vectorul este sortat crescător ​(pentru vectori sortaţi descrescător,​ raţionamentul este similar)+
- +
-Valoarea căutată''​x'',​ se compară cu valoarea cu indexul N/2 din vector, unde N este numărul ​de elemente. Dacă ''​x''​ este mai mic decât valoarea din vector, se caută în prima jumătate a vectorului, iar dacă este mai mare, în cea de-a doua jumătate. Căutarea ​în una dintre cele două jumătăţi se face după acelaşi algoritm.  +
- +
-Conceptualcăutarea binară este un algoritm recursiv, dar poate fi implementat la fel de bine într-un mod iterativ, folosind indecşii corespunzători bucăţii din vector în care se face căutareaAceşti indecşi se modifică pe parcursul algoritmuluiîntr-o buclă, în funcţie de comparaţiile făcute. Evoluţia algoritmului ​este ilustrată în imaginea de mai jos.  +
- +
-Pseudocodul pentru căutarea binară: +
 <code c> <code c>
-// cauta elementul x in vectorul sortat v, intre pozitiile 0 si n-1  si returneaza pozitia gasita sau -1 +// Vector cu cifrele unui nr intreg 
-int binary_search(int n, int v[NMAX], int x) { +int * cifre (int n) {  
-  int low = 0high = n - 1;+  int kc[5]// Vector local
  
-  ​while (low <high) { +  ​for (4; k >0; k--) { 
-    // De ce preferăm această formă față de (low + high) / 2 ? +    ​c[k] = n % 10
-    int middle ​low + (high low/ 2; +    ​10;
- +
-    ​if (v[middle] == x) { +
-      // Am gasit elementul, returnam pozitia sa +
-      return middle+
-    ​+
- +
-    if (v[middle] < x) { +
-      // Elementul cautat este mai mare decat cel curent, ne mutam in jumatatea +
-      // cu elemente mai mari +
-      low middle + 1; +
-    } else { +
-      ​// Elementul cautat este mai mic decat cel curent, ne mutam in jumatatea +
-      // cu elemente mai mici +
-      high = middle - 1; +
-    }+
   }   }
  
-  ​// Elementul nu a fost gasit +  return ​c// Aici este eroarea !
-  ​return ​-1;+
 } }
 </​code>​ </​code>​
  
-<note tip> +Astfel, o funcţie care trebuie să transmită ca rezultat un vector poate fi scrisă corect în două feluri: 
-Preferăm calcularea mijlocului intervalului [low, high] folosind formula ''​x = low + (high - low/ 2''​ deoarece formula perfect analogă ''​x = (low + high) / 2'' ​poate da overflow pentru valori mari ale low si high. De altfelacest bug a existat în biblioteca Java timp de 9 de ani. Putețciti mai mult despre asta în [[http://​googleresearch.blogspot.ro/​2006/​06/​extra-extra-read-all-about-it-nearly.html | acest articol]]. +  * Primeşte ca argument adresa vectorului (definit şi alocat în altă funcţie) şi depune rezultatele la adresa primită (soluţia recomandată!);  
-</​note>​+  * Alocă dinamic memoria pentru vector (folosind ​''​malloc''​)iar această alocare se menţine şla ieşirea din funcţie.
  
-=== Sortări ​===+==== Studiu de caz ====
  
-== Bubble Sort ==+<spoiler Pointeri la funcţii>​ 
 +Anumite aplicaţii numerice necesită scrierea unei funcţii care să poată apela o funcţie cu nume necunoscut, dar cu prototip şi efect cunoscut. ​
  
-Metoda bulelor este cea mai simplă modalitate de sortare ​unui vector, dar şi cea mai ineficientă. Ea funcţionează pe principiul parcurgerii vectorului şi comparării elementului curent ​cu elementul următor. Dacă cele două nu respectă ordinea, sunt interschimbate. Această parcurgere este repetată de suficiente ori până când nu mai există nici o interschimbare în vector+De exemplu, o funcţie care să calculeze integrala definită a oricărei funcţii cu un singur argument sau care să determine o radăcină reala a oricărei ecuaţii (neliniare)
  
-== Sortarea prin selecţie == +Aici vom lua ca exemplu o funcţie ''​listf''​ care poate afişa (lista) valorile unei alte funcţii cu un singur argumentîntr-un interval dat şi cu un pas datExemple ​de utilizare a funcţiei ''​listf''​ pentru afişarea valorilor ​unor funcţii de bibliotecă
- +<code c> 
-Sortarea prin selecţie oferă unele îmbunătăţiri în ceea ce priveşte complexitatea,​ însă este departe de fi considerat ​un algoritm eficient. Presupunând că se doreşte sortarea crescătoare a vectorului, se caută minimul din vector, şi se interschimbă ​cu primul element - cel cu indexul 0Apoi se reia acelaşi procedeu pentru restul vectorului. Motivul pentru care algoritmul ​de sortare prin selecţie este mai eficient este acela că vectorul în care se caută minimul devine din ce în ce mai mic, şi, evident, căutarea se face mai repede la fiecare pas. +int main(void{ 
- +  ​listf(sin, 0.0, 2.M_PI, M_PI 10.0); 
-Studiul ​unor algoritmi mai avansaţde sortare, precum şi studiul complexităţii lor nu constituie obiectul acestui laborator. Acestea se vor relua mai detaliat în cadrul altor cursuri ​(AA/PA).  +  ​listf(exp,​ 1.0, 20.01.0); 
- +   
-==== Exerciții Laborator CB/CD ==== +  ​return ​0;
- +
-  ​- Primul exercitiu presupune modificarea/​adaugarea de instructiuni unui cod existent pentru a realiza anumite lucruriIn momentul actual programul aduna la elementul curent vecinul din dreapta sa (daca acesta exista). +
-    ​*Nu uitati ca trebuie sa utilizam un [[http://​ocw.cs.pub.ro/​courses/​programare/​coding-style| coding style]] adecvat atunci cand scriem sursele+
-<code c ex1.c> +
-#include <stdio.h> +
- +
-#define N 100 +
- +
-void sum_right_neighbour(int v[N]int n) +
-+
-    int i+
- +
-    for (i = 0; i < n - 1; i++) { +
-        v[i] += v[i+1]; +
-    }+
 } }
 +</​code>​
  
-void print_vector(int v[N]int n+Problemele apar la definirea unei astfel de funcţii, care primeşte ca argument numele ​(adresa) unei funcţii. Prin convenţieîn limbajul C, numele unei funcţii neînsoţit de o listă de argumente şi de parantezele ''​()''​ specifice unui apel este interpretat ca un pointer către funcţia respectivă (fără a se folosi operatorul de adresare ''&''​). Deci ''​sin''​ este adresa funcţiei sin(x) în apelul funcţiei ''​listf''​. Declararea unui argument formal (sau a unei variabile) de tip pointer la o funcţie are forma următoare: 
-{ +<code c> 
-    int i;+tip (*pf) (lista_arg_formale) 
 +</​code>​ 
 +unde: 
 +  * ''​pf''​ este numele argumentului (variabilei) pointer la funcţie  
 +  * ''​tip''​ este tipul rezultatului funcţiei 
 +  
 +Parantezele sunt importante, deoarece absenţa lor modifică interpretarea declaraţiei. De exemplu, putem avea: 
 +<code c> 
 +tip * f(lista_arg_formale) // functie cu rezultat pointer, si NU pointer 
 +</​code>​
  
-    for (i = 0; i n; i++) { +În concluzie, definirea funcţiei ''​listf''​ este: 
- printf("%d ", v[i])+<code c> 
-    } +void listf (double (*fp)(double), double min, double max, double pas) { 
-    printf("​\n"​); +  ​double x,y;
-}+
  
-int main(void) +  for (x = min; x <= max; x = x + pas) { 
-+    ​y=(*fp)(x); // apel functie de la adresa din "​fp"​ 
-    ​int v[N] {1, 2, 3, 4, 5}; +    ​printf("​\n%20.10lf %20.10lf"​x, y); 
- +  }
-    print_vector(v, 5)+
-    sum_right_neighbour(v, 5); +
-    ​print_vector(v5); +
-     +
-    return 0;+
 } }
- 
 </​code>​ </​code>​
  
-Cerinte: +O eroare de programare ​care trece de compilare şi se manifestă la execuţie este apelarea unei funcţii fără paranteze; compilatorul nu apelează funcţia şi consideră că programatorul vrea să folosească adresa funcţieiDe exemplu
-  *Sa se creeze o functie ​care sa adauge la fiecare element din vector vecinul din stanga sa (daca acesta exista). +<code c
-  *Sa se creeze o functie care sa construiasca un alt vector ce contine in vector[i] produsul tuturor elementelor,​ mai putin elementul ​de pe pozitia ​din vectorul initial. +if (kbhit) 
-  *Se citesc de la tastatura caractere (se va citi cate un caracter pe rand). In functie de valoarea acestuia se va realiza una din actiunile urmatoare:​ +  ​break// echivalent cu if(1) break;
-    *q - iesire din program +
-    *m - eliminare element minim +
-    *M - eliminare element maxim +
-    *p - printare vector +
- +
- +
-**Următoarele două probleme vă vor fi date de asistent în cadrul laboratorului.** +
- +
-[[https://​drive.google.com/​file/​d/​16vwYGOWmvHDvj6F83lRFXXXtjbKo5ENl/​view?​usp=sharing|Checker laborator 5]] +
-[[ https://​ocw.cs.pub.ro/​courses/​programare/​checker | Tutorial folosire checker laborator ]] +
- +
- +
- +
-<spoiler Cum se foloseste checkerul+
-Pentru utilizarea checkerului:​ +
-  ​*Se va scrie cate un fisier sursa pentru fiecare problema; +
-  *La finalul fiecarui printf utilizat pentru afisarea rezultatului trebuie sa existe un newline; +
-  *Sursa nu trebuie sa contina alte printf-uri in afara de cele care scriu rezultatul asteptat la stdout. +
-  *Se va dezarhiva arhiva specifica exercitiului;​ +
-  *In directorul curent se afla checkerul, executabilul generat, folderele de input si output specifice problemei;​ +
-  *Se va rula “bash checker.sh <​executabil>​” unde <​executabil>​ este numele executabilului generat;+
  
 +if (kbhit()) ​
 +  break; // iesire din ciclu la apasarea unei taste
 +</​code>​
 </​spoiler>​ </​spoiler>​
-<​hidden>​ 
-Link direct către lista completă de probleme: [[https://​docs.google.com/​document/​d/​1NhP7zrR4sn4Tdq2JjEQEmSEdem-yB98RuvBilpFK0pk/​edit|aici]] 
-</​hidden>​ 
  
 +<spoiler Expresii complexe cu pointeri>​
 +Deşi sunt întâlnite mai rar în practică, limbajul C permite declararea unor tipuri de date complexe, precum: ​
  
-===== Exerciţii de Laborator ===== +<code c> 
-  - [4 x 1p] Se consideră un vector x cu n 100 componente. Să se determine: +char *(*(*x)())[10] 
-    - valoarea componentei minime;\\  +</code> ​
-    - poziţia componentei maxime;​\\ ​  +
-    - media aritmetică a componentelor;​\\ ​  +
-    - numărul componentelor mai mari ca media aritmetică.\\ Dimensiunea vectorului, n, şi elementele vectorului se citesc de la tastatură ​(cu scanf()).\\ Programul se va testa cu fişierul de intrare de mai jos (in.txt), utilizând comanda: <code bash> +
-./pb1 <in.txt >​out.txt +
-</​code>​\\ Fişierul de intrare, in.txt: <​code> ​  +
-50 +
-95 59 50 15 53 44 91 7 86 16 73 57 27 54 97 62 59 5 98 61 99 22 22 84 17 96 13 96 5 50 62 53 61 12 68 14 8 59 74 95 27 47 52 54 53 2 68 13 7 19  +
-</​code>​\\ Fişierul de ieşire asociat, out.txt: <​code>​ +
-min = 2 +
-poz_max = 20 +
-ma = 49.22 +
-gt_ma = 30 +
-</​code>​ +
-  - [2pSă se implementeze sortarea prin selecție pe un vector citit de la tastatură. +
-  - [2p] Citindu-se doi vectori ''​A''​ și ''​B''​ să calculeze diferența lor (elementele care sunt în ''​A'',​ dar nu sunt în ''​B''​).\\ Exemplu: ​<​code>​ +
-a = [6, 4, 2, 1, 7] +
-b = [4, 6, 3, 1]+
  
-Rezultat = [27] +În interpretarea acestor expresiioperatorii ''​()''​ şi ''​[]''​ au precedenţîn faţa ''​*''​ şi modul de interpretare al acestor expresii este pornind ''​din interior spre exterior''​Astfel expresia dată ca exemplu mai sus este (numerele de sub expresie ​reprezintă ​ordinea ​de interpretare):​ 
-</​code>​ +  - o variabila ''​x''​ 
-  - [2pAvând un vector de N elemente să se determine subsecvențsa de suma maximăO subsecvență a unui vector ​reprezintă ​un subșir ​de elemente ale acestuia aflate pe poziții consecutive. \\  +  - care este un pointer la o funcţie 
-<​hidden>​ +  - fără nici un parametru 
-Atentie! Nu vrem soluția optimă. Se punctează cu maxim șO(n^2). +  - şcare întoarce un pointer 
-</​hidden>​+  - la un vector de 10 elemente 
 +  - de tip pointer 
 +  - către tipul ''​char''​
  
-Exemplu: <​code>​ +Folosind acest procedeu, se pot rezolva şi alte situaţii aparent extrem de complexe: 
-+<​code ​c
-3 -4 2 3 // raspuns ​5 +    ​unsigned int *(* const *name[5][10] ) ( void );
-+
--3 7 -3 4 // raspuns 8 +
-+
--1 -2 -3 // raspuns -1+
 </​code>​ </​code>​
 +care semnifică o matrice de 5x10 de pointeri către pointeri constanţi la o funcţie, care nu ia nici un parametru, şi care întoarce un pointer către tipul ''​unsigned int''​.
 +</​spoiler>​
  
-== BONUS ==  +==== Exerciții ==== 
-- [2p] Implementați adunarea a două numere mari. Un număr mare va fi reprezentat ca un vector ''​A'',​ unde A[0] va fi numărul de cifre ale numărului, iar elementul A[i] va conține a ''​i''​-a cea mai nesemnificativă cifră. \\ Exemplu<​code>​ +Exercițiile pentru laborator se găsesc pe [[https://acs-pclp.github.io/laboratoare/05 | PCLP Laborator05:​ Pointeri. Abordarea lucrului cu tablouri folosind pointeri]].
-9420 // -> A[] = {4, 0, 2, 4, 9} +
-845 // -> B[= {3, 5, 4, 8} +
-// Suma +
-10265 // -> C[= {5, 5, 6, 2, 0, 1} +
-</​code>​+
  
-- [2p] Se citește de la tastatură un vector ''​V''​ sortat crescator de lungime ''​N''​ și apoi un șir de perechi ''​(x,​ y)''​ de numere naturale terminat cu ''​-1''​. Pe măsura citirii acestor numere, să se determine **eficient** numărul de elemente din ''​V''​ cuprinse între ''​x''​ și ''​y''​ (''​x < y''​). Folosiți o căutare binară modificată. \\ Exemplu: <​code>​ 
-5 
-2 10 20 50 80 
-5 15 
-1 // output 
-10 20 
-2 // output 
-19 91 
-3 // output 
--1 
-</​code>​\\ 
- 
-[[https://​drive.google.com/​open?​id=1SZfdFDKXvJGSfs0FILosxTiBYVksDn3D|Probleme laborator 14:​00-16:​00]] 
-===== Referinţe ===== 
-  * [[https://​github.com/​cs-pub-ro/​ComputerProgramming/​blob/​master/​Laboratories/​Lab5/​Lab5.pdf| Cheatsheet vectori]] 
-  * [[http://​en.wikipedia.org/​wiki/​Binary_search | Wikipedia - Căutare binară]] 
-  * [[http://​en.wikipedia.org/​wiki/​Sorting_algorithm |Wikipedia - Algoritmi de sortare]] 
  
 <​hidden>​ <​hidden>​
-===== Soluții ===== +==== Referinț=== 
-{{:​programare:​laboratoare:​5-solutions.zip|}}+TODO
 </​hidden>​ </​hidden>​
- 
- 
programare/laboratoare/lab05.1604565972.txt.gz · Last modified: 2020/11/05 10:46 by oana.balan
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0