Differences

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

Link to this comparison view

programare:laboratoare:lab08 [2016/11/23 13:11]
darius.neatu [Exemple de programe]
programare:laboratoare:lab08 [2020/10/05 00:37] (current)
darius.neatu [Pointeri. Abordarea lucrului cu tablouri folosind pointeri.]
Line 1: Line 1:
-===== Alocarea dinamică a memorieiAplicaţii folosind ​tablouri ​şi matrice. =====+===== PointeriAbordarea lucrului cu tablouri ​folosind pointeri. =====
  
-**Responsabil:** [[laura.vasilescu@cti.pub.ro|Laura Vasilescu]]+**Responsabili:*
 +  * [[neatudarius@gmail.com|Darius Neațu (CA 2019-2020)]] 
 +  * [[ion_dorinel.filip@cti.pub.ro|Dorinel Filip (CA 2019-2020)]] 
 +  ​* [[laura.vasilescu@cti.pub.ro|Laura Vasilescu]]
  
  
 ===== Obiective ===== ===== Obiective =====
  
-În urma parcurgerii acestui laborator studentul va fi capabil: +În urma parcurgerii acestui laboratorstudentul va fi capabil: 
-  * să aloce dinamic o zona de memorie; +  *  să înţeleagă noţiunea ​de pointer şi modurile în care se poate opera cu memoria în limbajul C 
-  * să elibereze o zona de memorie+  *  să cunoască modul în care pointerii sunt folosiţi în a returna sau modifica parametri în cadrul unei funcţii 
-  * să lucreze cu vectori ​şi matrice alocate dinamic.+  *  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
  
-===== Funcţii de alocare şi eliberare a memoriei =====+{{ http://​imgs.xkcd.com/​comics/​pointers.png}}
  
-Funcțiile standard de alocare și de eliberare a memoriei sunt declarate în fişierul antet ''​stdlib.h''​. ​ 
-  * ''​void* malloc(size_t size);''​ 
-  * ''​void* calloc(size_t nmemb, size_t size);''​ 
-  * ''​void* realloc(void *ptr, size_t size);''​ 
-  * ''​void free(void *ptr);''​ 
  
-==== Alocarea memoriei ​=====+===== Noţiunea de pointer ​=====
  
-Cele trei funcţii de alocare (''​malloc'',​ ''​calloc''​ și ''​realloc''​) au ca rezultat adresa zonei de memorie alocate (de tip ''​void*''​) şi ca argument comun dimensiunea,​ în octeţi, a zonei de memorie alocate (de tip ''​size_t''​ ). Dacă cererea de alocare nu poate fi satisfăcută pentru că nu mai există un bloc continuu de dimensiunea solicitată,​ atunci funcţiile de alocare au rezultat ''​NULL''​ (ce reprezintă un pointer ​de tip ''​void ​*''​ la adresa de memorie 0, care prin convenţie este o adresă ​nevalidă - nu există date stocate în acea zonă).+Un **pointer** este o variabilă ​care reţine o adresă ​de memorie.
  
-== Exemplu == +În C, un pointer poate reprezenta: 
-<code c>  +  - adresa unor date de un anumit tip  
-char *str = malloc(30); ​           // Aloca memorie pentru 30 de caractere +    tip elementar, structură, şir de caractere ​etc. 
-int *a = malloc(n ​sizeof(int));​ // Aloca memorie ​pt. n numere intregi +    operațiile cu pointeri sunt determinate de dimensiunea tipului de date 
-</​code>​+  - 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)
  
-<​note>​Dimensiunea ​memoriei luată ca parametru de ''​malloc()''​ este specificată ​în ''​octeţi'',​ indiferent de tipul de date care va fi stocat în acea regiune de memorie! Din acest motiv, pentru a aloca suficientă memorie, numărul dorit de elemente trebuie înmulţit ​cu dimensiunea unui element, atunci când are loc un apel ''​malloc()''​.</​note>​+<​note ​tip> 
 +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.
  
 +În cadrul laboratorului și al temelor de casă se vor utiliza mașini și pointeri pe 32 de biți.
 +</​note>​
  
-Alocarea de memorie pentru un vector şi iniţializarea zonei alocate cu zerouri se poate face cu funcţia ''​calloc''​.  +===== Operatori =====
-== Exemplu ​== +
-<code c> +
-int *acalloc(n, sizeof(int));​ // Aloca memorie pentru n numere intregi și inițializează zona cu zero +
-</​code>​+
  
-Codul de mai sus este perfect echivalent (dar mai rapid) cu următoarea secvenţă de instrucţiuni:​ +==== Operatorul ​de referențiere ====
-<code c> +
-int i; +
-int *a (malloc(n * sizeof(int));​ +
-for (i 0; i < n; i++) { +
-    a[i] 0; +
-+
-</​code>​+
  
-<​note>​În timp ce funcţia ​''​malloc()'' ​ia un singur parametru (o dimensiune ​în octeţi), funcţia ''​calloc()''​ primeşte două argumente, o lungime de vector şi o dimensiune ​fiecărui element. Astfel, această funcţie este specializată pentru memorie organizată ca un vector, în timp ce ''​malloc()''​ nu ţine cont de structura memoriei.</​note>​ +**''​&''​** - apare în fațvariabilei asupra căreia acționează
-<note warning>​Acest lucru aduce și o măsură de siguranță în plus deoarece înmulțirea dintre numărul de elemente și dimensiunea tipului de date ar putea face overflow, iar dimensiunea memoriei alocate să nu fie în realitatea cea așteptată.</​note>​+
  
-Realocarea unui vector care creşte (sau scade) faţă ​de dimensiunea estimată anterior se poate face cu funcţia ''​realloc''​care primeşte adresa veche şi noua dimensiune şi întoarce ​**noua adresă**+Este aplicat unei **variabile,​ avand ORICE tip de date,** și **obține ADRESA de (din) memorie** a variabilei respective.
-<code c> +
-int *aux; +
-aux = realloc ​(a, 2 sizeof(int));​ // Dublare dimensiune anterioara (n) +
-if (aux) //daca aux este diferit de NULL +
-    ​a=aux; +
-else +
-    //​prelucrare in caz de eroare +
-</​code>​+
  
-În exemplul anterior, noua adresă este memorată tot în variabila pointer ''​a'',​ înlocuind vechea adresă (care nu mai este necesară şi nici nu mai trebuie folosită). Funcţia ''​realloc()''​ realizează următoarele operaţii:​ +==== Operatorul ​de dereferențiere ====
-  * alocă o zonă de dimensiunea specificată ca al doilea argument  +
-  * copiază la noua adresă datele de la adresa veche (primul argument al funcţiei)  +
-  * eliberează memoria de la adresa veche. +
-In cazul in care nu reuseste sa aloce memorie functia ''​realloc()''​ intoarce NULL lasand pointerul initial nemodificat. Din acest motiv verificam daca realocarea a reusit si apoi asignam rezultatul pointerului ''​a''​. ​+
  
-==== Eliberarea memoriei =====+**''​*''​** - apare în fața variabilei asupra căreia acționează
  
-Funcţia ''​free()''​ are ca argument o adresă (un pointer) şeliberează zona de la adresa respectivă (alocată prin apelul unei funcţii ​de tipul ''​[m|c|re]alloc''​). Dimensiunea zonei nu mai trebuie specificată deoarece este ţinută minte de sistemul de alocare de memorie în nişte structuri interne.+Este aplicat unei **variabile de tip pointer** ș**obține valoarea stocată** la adresa respectivă (indicata ​de pointer).
  
-===== Vectori alocaţi dinamic =====+<note warning>​ 
 +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. 
 +</​note>​
  
-Structura de vector are avantajul simplităţii şi economiei de memorie faţă de alte structuri de date folosite pentru memorarea unei colectii de informaţii între care există anumite relaţii. Între cerinţa de dimensionare constantă a unui vector şi generalitatea programelor care folosesc astfel de vectori există o contradicţie. De cele mai multe ori programele pot afla (din datele citite) dimensiunile vectorilor ​cu care lucrează şi deci pot face alocare dinamică a memoriei pentru aceşti vectori. Aceasta este o solutie mai flexibilă, care foloseşte mai bine memoria disponibilă şi nu impune limitări arbitrare asupra utilizării unor programeÎn limbajul C nu există practic nici o diferenţă între utilizarea ​unui vector cu dimensiune fixă şi utilizarea unui vector ​alocat dinamic, ceea ce încurajează şi mai mult utilizarea unor vectori cu dimensiune variabilă.+Pentru ca dereferențierea să aibă loc cu succes, pointer-ul trebuie să indice ​adresă de memorie validă, la care programul are accesAceastă adresă poate fi adresa unei variabile declarate în prealabil sau adresa ​unui bloc de memorie ​alocat dinamic ​(după cum vom vedea mai departe).
  
-== Exemplu ​==+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. 
 + 
 + 
 +==== Particularități ​====
 <code c> <code c>
-int main() { +int *a;             /* Pointer */ 
-  int n,i+int b = 5         /* Variabila */ 
-  ​int ​*a                    ​// Adresa vector alocat dinamic+char *c           /* Pointer catre un caracter (sau sir de caractere) */ 
 +void *buff = NULL;  /* Pointer catre void, initializat la NULL */
  
-  printf("​n = ");  +/* Asignare NEVALIDAa este un pointer neinitializat */ 
-  ​scanf("​%d",​ &n)           // Dimensiune vector+*a = 1
  
-  a = calloc(n, sizeof(int)); ​// Alternativ: ​= malloc(n ​sizeof(int));​ +/ia adresa variabilei b */      ​ 
-  ​printf("​Componente vector: \n"​);​ += &b;
-   +
-  for (i 0; i < n; i++) { +
-    scanf("​%d", ​&a[i])      // Sau scanf (“%d”, a+i); +
-  } +
-  for (i = 0; i < n; i++) {    // Afisare vector +
-    printf("​%d ",​a[i]);​ +
-  }+
  
-  free(a);                    // Nu uitam sa eliberam memoria +/* Continutul memoriei de la adresa a (care fost initializata mai sus 
- +   ia valoarea 5. Acest lucru este echivalent cu "b = 5;"  
-  return 0; + *
-}+*a = 5  
 </​code>​ </​code>​
  
-Există ​şi cazuri în care datele memorate într-un vector rezultă din anumite prelucrăriiar numărul lor nu poate fi cunoscut de la începutul execuţiei. Un exemplu poate fi un vector cu toate numerele prime mai mici ca o valoare dată. În acest caz se poate recurge la o realocare dinamică a memoriei. În exemplul următor se citeşte un număr necunoscut de valori întregi într-un vector extensibil:+În cazul declaraţiilor de pointeri, operatorul ''​*''​este asociat numelui variabilei, ​şi nu numelui tipuluiastfel 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>
-#define INCR 100 // cu cat creste vectorul la fiecare realocare+/* sir1 e pointer, sir2 si sir3 sunt caractere */ 
 +char *sir1, sir2, sir3; 
  
-int main() { +/* a, b si c sunt pointeri */ 
-  int ni, m; +int *a*b, *c;
-  float x, *v;                   // v = adresa vector +
-  n = INCR;   +
-  i = 0;+
  
-  v = malloc(n ​sizeof(float))// Dimensiune initiala vector +/Doar a este pointerformatarea codului ​este nerecomandata *
-   +char* ab
-  while (scanf("​%f",​ &x) != EOF) { +</​code>​
-    if (++i == n) {              // Daca este necesar... +
-      n = n + INCR;              ​// ... creste dimensiune vector +
-      v = realloc(vectorn * sizeof(float))+
-    }+
  
-    v[ix                   // Memorare in vector numar citit  ​ +Operatorul ''​*''​ poate fi folosit şî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>​
  
-  m = i; +<note warning> 
- +Un pointer către ''​void''​ nu poate fi folosit direct în operaţii cu pointeri, ci trebuie convertit mai întâi la un pointer către un tip de date.  
-  for (i = 0; i mi++) {       // Afisare vector +</note> 
-    printf("​%f ",v[i])+De exemplu: 
-  } +<code c> 
- +void *mem; 
-  free(vector); +//[...] 
- +*mem = 10       // Operatie ILEGALA 
-  return 0; +((int *)mem) = 10// Operatie legala, dar riscanta
-}+
 </​code>​ </​code>​
- +===== Tipuri de pointeri ​=====
-===== Matrice alocate dinamic

 ​===== +
- +
-Alocarea dinamică pentru o matrice este importantă deoarece: +
-  * foloseşte economic memoria şi evită alocări acoperitoare,​ estimative.  +
-  * permite matrice cu linii de lungimi diferite (denumite uneori ''​ragged arrays'',​ datorită formelor "​zimţate"​ din reprezentările grafice)  +
-  * reprezintă o soluţie bună la problema argumentelor de funcţii de tip matrice.+
    
-Daca programul poate afla numărul efectiv de linii şi de coloane al unei matrice (cu dimensiuni diferite de la o execuţie la alta), atunci se va aloca memorie pentru un vector de pointeri (funcţie de numărul liniilor) şi apoi se va aloca memorie pentru fiecare linie (funcţie de numărul coloanelor) cu memorarea adreselor liniilor în vectorul de pointeri. O astfel de matrice se poate folosi la fel ca o matrice declarată cu dimensiuni constante.+==== Pointeri ​la date ====
  
-== Exemplu ​==+== Stocarea datelor la o anumită adresă; Citirea datelor de la o anumită adresă ​==
 <code c> <code c>
-int main () { +*p = y;         // Ia valoarea y si pune-o la adresa indicata de p 
-  ​int ​**a+x = *p        // Ia valoarea de la adresa indicata de p si pune-o in variabila x 
-  int i, j, nl, nc;+*s1++ = *s2++ 
 +</​code>​
  
-  printf("​nr. linii ")+== Atribuirea unei adrese unui pointer ==  
-  scanf(“%d”, &nl);+<code c> 
 +p1 = p2
 +p = NULL; 
 +p = malloc(n); // Veti studia malloc() intr-un laborator ulterior 
 +</​code>​
  
-  printf("​nr. coloane ​"); +== Interpretarea diferită a datelor din memorie == 
-  scanf(“%d”, ​&nc);+<code c> 
 +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>​
  
-  a malloc(nl * sizeof(int *))  // Alocare pentru vector de pointeri+== Aritmetică cu pointeri == 
 +Adunarea sau scăderea unui întreg la un pointer, incrementarea sau decrementarea unui pointer. Aceste 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:  
 +<code c> 
 +int *num;
  
-  ​for (i = 0; i < n; i++) { +/* Aduna la adresa initiala dimensiunea tipului de date referit  
-    a[i] = calloc(nc, ​sizeof(int)); // Alocare pentru o linie si initializare ​la zero +   ​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++;
  
-  ​// Completare diagonala matrice unitate +/* Incrementeaza adresa cu 5 * sizeof(int) *
-  for (i 0nl; i++) { +num num + 5 
-    ​a[i][i] ​1;                    // a[i][j]=0 pentru i !j    +</code> 
- +==== Pointeri la tablouri ====
  
-  /Afisare matrice +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:​ 
-  ​printmat(a, nl, nc);+<code c> 
 +void printVec(int a[], int n); 
 +</code> 
 +sau 
 +<code c> 
 +void printVec(int *a, int n); 
 +</​code>​
  
-  ​for (i = 0; i < nl; i++) +Î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'':​ 
-     free(a[i]); +<code c> 
-  free(a);                         // Nu uitam sa eliberam! ​ +void printVec (int a[], int n)  
- +
-  ​return ​0;+  int i; 
 +  ​for (i = 0; i < n; i++) 
 +    ​printf("​%6d", ​a[i]); ​// Indexare 
 +
 +</​code>​ 
 +sau 
 +<code c> 
 +void printVec ​(int *a, int n)  
 +{ 
 +  ​int i; 
 +  for (i = 0; i < n; i++) 
 +    printf("​%6d",​ *a++); // Indirectare
 } }
 </​code>​ </​code>​
  
-Funcţia de afişare ​matricei se poate defini astfel:+Astfel, există următoarele echivalenţde notaţii pentru un vector ''​a''​:
 <code c> <code c>
-void printmat(int ​**a, int nl, int nc{ +a[0] <​==> ​*
-  for (i = 0; i nl; i++) { +a[1] <​==> ​*(+ 1
-    for (j = 0; j nc; j++) { +a[k] <==> *(a k
-      ​printf("​%2d", ​a[i][j]); +&a[0<==> a 
-    }+&a[1<==> a + 1 
 +&a[k<==> a + k 
 +</​code>​
  
-    printf("​\n"​); +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 C ş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
 +p = a; ++p; //corect 
 +a = p; ++a; //EROARE
 </​code>​ </​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.
  
-Notaţia ''​a[i][j]'' ​este interpretată astfel pentru ​matrice alocată dinamic: +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.
-  * ''​a[i]''​ conţine un pointer ​(o adresă ''​b''​) +
-  * ''​b[j]''​ sau ''​b+j''​ conţine întregul din poziţia ''​j''​ a vectorului cu adresa ''​b''​.+
  
-Astfel, ''​a[i][j]''​ este echivalent semantic cu expresia cu pointeri ''​*(*(a + i) + j)''​.+==== Pointeri în funcţii ====
  
-Totuşi, funcţia ''​printmat()''​ dată anterior nu poate fi apelată dintr-un program ​care declară argumentul efectiv ca o matrice cu dimensiuni constante. Exemplul următor este corect sintactic dar nu se execută corect:+Î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;
 +}
 +
 +// Utilizarea functiei
 int main() { int main() {
-  int x[2][2] ​{ {1, 2}, {34} }; // O matrice patratica cu 2 linii si 2 coloane  ​ +  int 10; 
-  ​printmat((int**)x2, 2);  ​+  int m = 15; 
 +   
 +  incmod(&​nm)  
 +  ​// Afisam noua valoare a lui n 
 +  ​printf("n: %d"n); 
 +  
   return 0;   return 0;
-} 
 </​code>​ </​code>​
  
-Explicaţia este interpretarea diferită a conţinutului zonei de la adresa aflată în primul argument: funcţia ''​printmat()''​ consideră că este adresa unui vector de pointeri ​(''​int *a[]''​), iar programul principal consideră că este adresa unui vector de vectori (''​int x[][2]''​),​ care este reprezentat liniar in memorie.+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]=ht[1]=m, t[2]=s 
 +</​code>​
  
-Se poate defini şi o funcţie ​pentru alocarea de memorie la execuţie pentru o matrice.+funcţie ​poate avea ca rezultat un pointer, dar acest pointer nu trebuie să conţină adresa unei variabile locale. De obicei, rezultatul pointer este egal cu unul din argumente, eventual modificat în funcţie. De exemplu: 
 +<code c> 
 +// Incrementare pointer p 
 +char *incptr(char *p) { 
 +  return ++p; 
 +
 +</​code>​
  
-== Exemplu ==+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ţiei, pentru a fi folosită ulterior. De exemplu, următoarea secvenţă de cod este ''​greşită'':​
 <code c> <code c>
-int **newmat(int nl, int nc) { // Rezultat adresa matrice
 +// Vector cu cifrele unui nr intreg 
-  int i; +int * cifre (int n) {  
-  int **p = malloc(nl * sizeof(int *));+  int k, c[5]// Vector local
  
-  for (= 0; i < n; i++) { +  for (k = 4; k >= 0; k--) { 
-    ​p[i] = calloc(nc, sizeof(int));+    ​c[k] = n % 10; 
 +    n = n / 10;
   }   }
  
-  return ​p;+  return ​c// Aici este eroarea !
 } }
 </​code>​ </​code>​
  
-===== Stil de programare ===== +Astfel, o funcţie care trebuie să transmită ca rezultat un vector poate fi scrisă corect în două feluri: 
-==== Exemple de programe ====+  * Primeşte ca argument adresa vectorului (definit şi alocat în altă funcţie) şi depune rezultatele la adresa primită (soluţia recomandată!);​  
 +  * Alocă dinamic memoria pentru vector (folosind ''​malloc''​),​ iar această alocare se menţine şi la ieşirea din funcţie.
  
-**''​Exemplul 1''​**:​ Funcţie echivalentă cu funcţia ​de bibliotecă ''​strdup()'':​ +==== Studiu ​de caz ====
-<code c>  +
-#include <​string.h>​ +
-#include <​alloc.h>​+
  
-// Alocare memorie si copiere sir +<spoiler Pointeri la funcţii>​ 
-char *strdup(char* adr) { +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. ​
-  int len = strlen(adr);​ +
-  char *rez = malloc(len);​+
  
-  strcpy(rezadr);+De exempluo 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)
  
-  ​return ​rez;+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 dat. Exemple de utilizare a funcţiei ''​listf''​ pentru afişarea valorilor unor funcţii de bibliotecă:​ 
 +<code c> 
 +int main(void) { 
 +  listf(sin, 0.0, 2.0 * M_PI, M_PI / 10.0); 
 +  listf(exp, 1.0, 20.0, 1.0); 
 +   
 +  ​return ​0;
 } }
 </​code>​ </​code>​
  
 +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> <code c>
-// Utilizare "​strdup"​ +tip (*pf) (lista_arg_formale) 
-#​include ​<stdio.h+</code
- +unde: 
-int main() { +  * ''​pf''​ este numele argumentului ​(variabileipointer la funcţie ​ 
-  ​char s[80], ​*d; +  * ''​tip''​ este tipul rezultatului funcţiei 
- +  
-  do { +Parantezele sunt importante, deoarece absenţa lor modifică interpretarea declaraţiei. De exemplu, putem avea: 
-    if (gets(s) == 0) { +<code c> 
-      ​break;​ +tip * f(lista_arg_formale// functie cu rezultat pointer, si NU pointer
-    } +
-    d = strdup(s)+
-    puts(d); +
-    free(d); +
-  } while (1); +
- +
-  return 0; +
-}+
 </​code>​ </​code>​
  
-**'''​Exemplul 2''**Vector alocat dinamic (cu dimensiune cunoscută la execuţie)+În concluzie, definirea funcţiei ​''​listf'' ​este:
 <code c> <code c>
-#include <​stdio.h>​ +void listf (double (*fp)(double),​ double min, double max, double pas) { 
-#include <​stdlib.h>​+  ​double x,y;
  
-int main() { +  for (x = minx <= maxx + pas{ 
-  int n, i; +    y=(*fp)(x); // apel functie de la adresa din "​fp"​ 
-  int *a// Adresa vector +    ​printf("\n%20.10lf %20.10lf", ​x, y);
- +
-  printf("​n="); +
-  ​
scanf("​%d",​ &n); // Dimensiune vector +
- +
-  a malloc(sizeof(int)); +
-  printf("​componente vector: \n"); +
- +
-  for (i = 0; i < n; i++) {       // Citire vector +
-    ​scanf("%d", ​&a[i]);+
   }   }
- 
-  for (i = 0; i < n; i++) {       // Afisare vector 
-    printf("​%d",​ a[i]); 
-  } 
- 
-  free(a); 
- 
-  return 0; 
 } }
 </​code>​ </​code>​
  
-**''​Exemplul 3''​**:​ Vector realocat dinamic (cu dimensiune necunoscută+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ţiei. De exemplu:
 <code c> <code c>
-#include <​stdio.h>​ +if (kbhit) 
-#include <​stdlib.h>​+  ​break;​ // echivalent cu if(1) break;
  
-#define INCR 4+if (kbhit())  
 +  break; // iesire din ciclu la apasarea unei taste 
 +</​code>​ 
 +</​spoiler>​
  
-int main() { +<spoiler Expresii complexe cu pointeri>​ 
-  int n, i, m; +Deşsunt întâlnite mai rar în practicălimbajul C permite declararea unor tipuri de date complexeprecum: ​
-  float x*v;+
  
-  n = INCR; +<code c> 
-  i = 0;+char *(*(*x)())[10] 
 +</​code> ​
  
-  v = (malloc(n ​sizeof(float));+În interpretarea acestor expresii, operatorii ''​()''​ şi ''​[]''​ au precedenţa î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)
 +  - o variabila ''​x''​ 
 +  - care este un pointer la o funcţie 
 +  - fără nici un parametru 
 +  - şi care întoarce un pointer 
 +  - la un vector de 10 elemente 
 +  - de tip pointer 
 +  - către tipul ''​char''​
  
-  while (scanf("​%f"​&x) != EOF) { +Folosind acest procedeuse pot rezolva şalte situaţii aparent extrem de complexe: 
-    if (== n) { +<code c> 
-      n = n + INCR; +    ​unsigned int *(* const *name[5][10] ) ( void );
-      v = (realloc(v, n sizeof(float));​ +
-    } +
-    +
-    v[i++= x; +
-  } +
- +
-  m = i; +
- +
-  for (i = 0; i < m; i++) { +
-    printf("​%.2f ", v[i])+
-  } +
-   +
-  free(v); +
- +
-  return 0; +
-}+
 </​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>​
 +==== Exerciții Laborator CB/CD ====
 +Primul exercițiu presupune rularea unei secvente de cod cu scopul de a clarifica diverse aspecte legate de pointeri. Analizați fiecare intrebare si incercati sa intuiti ce ar trebui sa se afiseze in continuare. După aceea verificați
  
-**''​Exemplul 4''​**:​ Matrice alocată dinamic (cu dimensiuni cunoscute la execuţie) 
  
-<code c>+<​code ​c ex.c>
 #include <​stdio.h>​ #include <​stdio.h>​
-#include <​stdlib.h>​ 
  
-int main() { +void next(void) 
-  int n, i, j; +
-  int **mat; // Adresa matrice + fflush(stdout); 
-  + getchar(); 
-  // Citire dimensiuni matrice +}
-  printf("n = "); +
-  ​
scanf("​%d",​ &n);+
  
-  // Alocare memorie ptr matrice +int main(void) 
-  mat = malloc(n * sizeof(int *));+
 + int a[10];
  
-  for (i = 0; i < n; i++{ + printf("a[0] = ?")next(); 
-    mat[i] = calloc(n, sizeof(int)); + printf("​a[0] = %d\n"a[0]); next();
-  }+
  
-  // Completare matrice + printf("​*a ​?")next()
-  for (0i < ni++) { + printf("​*a ​%d\n", *a)next();
-    ​for ​(0; j < n; j++{
 +
-      mat[i][j] = n * i + j + 1; +
-    } +
-  }+
  
-  // Afisare matrice + printf("​a ​?"); next(); 
-  for (0; i < n; i+++ printf("​a = %p\n", ​a); next();
-    for (j = 0;j < n; j++{ +
-      printf("​%6d", ​mat[i][j]); +
-    }+
  
-    ​printf("​\n"​);​ + printf("​&a = ?"); next(); 
-  }+ printf("&​a = %p\n", &a); next();
  
-  return ​0; + printf("​sizeof(a[0]) = ?"); next()
-+ printf("​sizeof(a[0]) = %ld\n",​ sizeof(a[0]));​ next();
-</​code>​+
  
-**'''​Exemplul 5''​**:​ Vector de pointeri la şiruri alocate dinamic + printf("​sizeof(*a) = ?"); next(); 
-<code c>  + printf("​sizeof(*a) = %ld\n",​ sizeof(*a)); next();
-/Creare / afisare vector de pointeri la siruri ​*/+
  
-#include <​stdio.h>​ + printf("​sizeof(a) = ?"); next(); 
-#include <​stdlib.h>​ + printf("​sizeof(a) = %ld\n",​ sizeof(a)); next();
-#include <​string.h>​+
  
-// Afisare siruri reunite in vector de pointeri + printf("​sizeof(&​a) = ?"); next(); 
-void printstr(char *vp[], int n) + printf("​sizeof(&​a) = %ld\n", sizeof(&​a));​ next();
-  int i;+
  
-  for (0i < n; i++{ + printf("*a + 1 ?")next(); 
-    printf("​%s\n", ​vp[i]); + printf("​*a + 1 = %d\n", ​*a + 1); next();
-  } +
-}+
  
-// Ordonare vector de pointeri la siruri + printf("*a + 2 = ?"); next(); 
-void sort(char *vp[], int n+ printf("​*a + 2 = %d\n", *a + 2); next();
-  int i, j+
-  ​char ​*tmp;+
  
-  for (1j < nj++) { + printf("a + 1 ?")next()
-    ​for ​(0; i < - 1; i++) { + printf("a + 1 %p\n"+ 1); next();
-      if (strcmp(vp[i]vp[i+1]> 0+
-        tmp = vp[i]; +
-        vp[i] = vp[i+1]; +
-        vp[i+1] = tmp; +
-      } +
-    } +
-  } +
-}+
  
-// Citire siruri si creare vector de pointeri + printf("a + 2 ?"); next()
-int readstr ​(char * vp[]) { + printf("​a + 2 = %p\n"a + 2); next();
-  int n 0+
-  char *p, sir[80];+
  
-  while (scanf("%s", sir== 1{ + printf("&a + 1 = ?"); next(); 
-    p = malloc(strlen(sir) ​+ 1); + printf("&​a + 1 = %p\n", &​a ​+ 1); next();
-    strcpy(p, sir); +
-    vp[n] = p; +
-    ++n; +
-  }+
  
-  return n; + printf("&​a + 2 = ?"); next(); 
-+ printf("&​a + 2 = %p\n"&a + 2); next();
- +
-int main() +
-  int nchar *vp[1000]; // vector de pointeri, cu dimensiune fixa +
- +
-  n = readstr(vp); // citire siruri si creare vector +
-  sort(vpn); // ordonare vector +
-  printstr(vp, n); // afisare siruri+
  
-  retrun ​0;+ return ​0;
 } }
 </​code>​ </​code>​
 +**Următoarele două probleme vă vor fi date de asistent în cadrul laboratorului.**
  
-==== Practici recomandate ====+[[https://​drive.google.com/​drive/​u/​0/​folders/​1qB6EZLGVubKbuTXMtMue06egH_8fo25M|Checker laborator 8]] 
 +[[ https://​ocw.cs.pub.ro/​courses/​programare/​checker | Tutorial folosire checker laborator ]] 
 +<​hidden>​
  
-
Deşi au fost enunţate în momentul în care au fost introduse noţiunile corespunzătoare în cursul acestui material, se pot rezuma câteva reguli importante de folosire a variabilelor de tip pointer: +Link direct ​către ​lista completă de probleme: [[https://​docs.google.com/​document/​d/​1nERoSfIgBICsjXPB7SE5fH7Spq3WwlOzWNes6Eivp48/​edit|aici]] 
-  * Aveţi grijă ca variabilele de tip pointer să indice ​către ​adrese de memorie valide înainte de a fi folosite; consecinţele adresării unei zone de memorie aleatoare sau nevalide (''​NULL''​) pot fi dintre cele mai imprevizibile +</​hidden>​ 
-  * Utilizaţi o formatare a codului care să sugereze asocierea operatorului ''​*''​ cu variabila asupra căreia operează; acest lucru este în special valabil pentru declaraţiile de pointeri.  +===== Exerciţii de Laborator =====
-  * Nu returnaţi pointeri la variabile sau tablouri definite în cadrul funcţiilor,​ întrucât valabilitatea acestora încetează odată cu ieşirea din corpul funcţiei. +
-  * Verificaţi rezultatul funcţiilor ​de alocare a memoriei, chiar dacă dimensiunea pe care doriţi s-o rezervaţi este mică. Atunci când memoria nu poate fi alocată rezultatul este ''​NULL''​ iar programul vostru ar trebui să trateze explicit acest caz (finalizat, de obicei, prin închiderea "​curată"​ a aplicaţiei).  +
-  * Nu uitaţi să eliberaţi memoria alocată dinamic, folosind funcţia ''​free()''​. Memoria rămasă neeliberată încetineşte performanţele sistemului şi poate conduce la erori (bug-uri) greu de depistat.+
  
-===== Anexă: Clase de stocare =====+  -  **[3p]** În reprezentarea unui număr întreg pe mai mulţi octeţi (de exemplu un short sau un int), se pune problema ordinii în care apar octeţii în memorie. Astfel, există două moduri de reprezentare: 
 +     * Big-endian (în care primul octet din memorie este cel mai semnificativ) 
 +     * Little-endian (în care primul octet din memorie este cel mai puţin semnificativ)\\ ​   **Se cere** să se scrie un program care să determine endianess-ul calculatorului pe care este compilat şi rulat, şi să afişeze un mesaj corespunzător pe ecran. 
 +  - În limbajul C, şirurile ​de caractere sunt reprezentate în memorie ca o succesiune de valori de tip char terminate printr-un caracter special NULL ('​\0'​). Șirurile de caractere sunt pasate ca parametri printr-un pointer la primul caracter din şir, şi sunt prelucrate până când se întâlneşte caracterul nul, indiferent care este lungimea reală a zonei alocate. Astfel, un anumit vector de caractere de lungime N poate stoca şiruri de caractere de lungime între 0 şi N-1 (pentru că nu considerăm şi caracterul nul ca făcând parte din conţinutul şirului). 
 +     * **[1p]** Să se scrie o funcţie care calculează lungimea unui şir de caractere dat ca parametru<​code>​int str_length(char *s);</​code>​ 
 +     * **[1p]** Să se scrie o funcţie pentru ştergerea (eliminarea) a n caractere dintr-o poziţie dată a unui şir ce returnează adresa șirului de caractere modificat<​code>​char * strdel(char *s, int pos, int n);</​code>​ 
 +     * **[1p]** Să se scrie o funcţie pentru inserarea unui şir ''​s2''​ într-o poziţie dată ''​pos''​ dintr-un şir ''​s1''​. Se va presupune că există suficient loc în vectorul lui ''​s1''​ pentru a face loc şirului ''​s2''​. Funcţia returnează adresa şirului ''​s1''​.<​code>​char * strins(char *s1, int pos, char *s2)</​code>​ 
 +     * **[1p]** Scrieţi o funcţie care stabileşte dacă un şir dat (format din caractere alfanumerice) este egal cu o mască, ce poate conţine caractere alfanumerice şi caracterul special '?'​. Se consideră că acest caracter înlocuieşte orice alt caracter alfanumeric. De exemplu, "​abcde"​ este echivalent cu "?​bc?​e"​.<​code>​int eq_mask(char *sir, char *masca);</​code>​ 
 +     * **[2p]** Scrieţi o funcţie care stabileşte dacă un cuvânt dat se găseşte sau nu într-un tablou de cuvinte.<​code>​int eqcuv(char *cuv, char **tablou);</​code>​ Pentru testare folosiți următoarea funcție ''​main'':<​code>​ 
 +int main(void) { 
 + char *tablou[100] ​{"​curs1",​ "​curs2",​ "​curs3"​};​ 
 + char *cuv1 "​curs2",​ *cuv2 "​curs5";​ 
 + printf("​curs2 %s in tablou\n",​(eqcuv(cuv1,​ tablou)) ? "​este"​ : "nu este"​);​ 
 + printf("​curs5 %s in tablou\n",​(eqcuv(cuv2,​ tablou)) ? "​este"​ : "nu este"​);​  
 +}</​code>​ 
 +  - **[3p]** Scrieţi un program care afişează valorile functiilor ''​sqrt'',​ ''​sin'',​ ''​cos'',​ ''​tan'',​ ''​exp''​ şi ''​log'',​ în intervalul [1..10], cu pasul 0.1. În acest scop, se creează un tablou de pointeri la aceste funcţii şi se apelează funcţiile în mod indirect prin aceşti pointeri.
  
-<​note>​Această secţiune este opţională şi nu este necesară pentru rezolvarea exerciţiilor de laborator, însă ajută la înţelegerea aprofundată a modului în care limbajul C lucrează cu variabilele.</​note>​ 
  
-

Clasa de stocare (memorare) arată când, cum şi unde se alocă memorie pentru o variabilă (vector). Orice variabilă C are o clasă de memorare care rezultă fie dintr-o declaraţie explicită, fie implicit din locul unde este definită variabila. 
Există trei moduri de alocare a memoriei, dar numai două corespund unor clase de memorare: 
-  * ''​Static'':​ memoria este alocată la compilare în segmentul de date din cadrul programului şi nu se mai poate modifica în cursul execuţiei. Variabilele externe, definite în afara funcţiilor,​ sunt implicit statice, dar pot fi declarate static şi variabile locale, definite în cadrul funcţiilor. ​ 
-   * ''​Automat'':​ memoria este alocată automat, la activarea unei funcţii, în zona stivă alocată unui program şi este eliberată automat la terminarea funcţiei. Variabilele locale unui bloc (unei funcţii) şi argumentele formale sunt implicit din clasa ''​auto''​. ​ 
-  * ''​Dinamic'':​ memoria se alocă la execuţie în zona ''​heap''​ alocată programului,​ dar numai la cererea explicită a programatorului,​ prin apelarea unor funcţii de bibliotecă (''​malloc'',​ ''​calloc'',​ ''​realloc''​). Memoria este eliberată numai la cerere, prin apelarea funcţiei ''​free''​. Variabilele dinamice nu au nume şi deci nu se pune problema clasei de memorare (atribut al variabilelor cu nume). 
-  
-
Variabilele statice pot fi iniţializate numai cu valori constante (pentru că se face la compilare), dar variabilele auto pot fi iniţializate cu rezultatul unor expresii (pentru că se face la execuţie). Toate variabilele externe (şi statice) sunt automat iniţializate cu valori zero (inclusiv vectorii). 

Cantitatea de memorie alocată pentru variabilele cu nume rezultă automat din tipul variabilei şi din dimensiunea declarată pentru vectori. Memoria alocată dinamic este specificată explicit ca parametru al funcţiilor de alocare. 
  
-

O a treia clasă de memorare este clasa ''​register''​ pentru variabile cărora, teoretic, li se alocă registre ale procesorului şi nu locaţii de memorie, pentru un timp de acces mai bun. În practică nici un compilator modern nu mai ţine cont de acest cuvânt cheie, folosind automat registre atunci când codul poate fi optimizat în acest fel (de exemplu când observă că nu se accesează niciodata adresa variabilei în program).+<​hidden>​ 
 +==== Soluții ====
  
-
Memoria neocupată de datele statice şi de instrucţiunile unui program este împărţită între stivă şi heapConsumul de memorie pe stivă este mai mare în programele cu funcţii recursive şi număr mare de apeluri recursive, iar consumul de memorie heap este mare în programele cu vectori şi matrice alocate (şi realocate) dinamic.

+{{:​programare:​laboratoare:​7-solutions.zip|}}
  
-===== Exercitii laborator CB/CD ===== 
-Codul sursa se gaseste [[http://​swarm.cs.pub.ro/​~gmuraru/​PC/​ex.c|aici]] 
- 
-Primul exercitiu presupune modificarea/​adaugarea de instructiuni unui cod existent pentru a realiza anumite lucruri. In momentul actual programul citeste o matrice si afiseaza suma elementelor de pe fiecare linie. 
-  * Nu uitati ca trebuie sa utilizam un coding style adecvat atunci cand scriem sursele. 
- 
-Cerinte: 
-  *Sa se mute elementele de pe o anumita linie, intr-un vector, alocat dinamic: 
-  *Sa se mareasca dimensiunea matricea astfel incat sa aiba o linie in plus, iar pointerul specific ultimei linii sa indice spre vectorul generat anterior. 
- 
- 
-**Următoarele două probleme vă vor fi date de asistent în cadrul laboratorului.** 
-<​hidden>​ 
-Link direct către lista completă de probleme: [[https://​docs.google.com/​document/​d/​15IBKbSiFYEJ_9zeuMksWYnFt2WkKgC1EaigCpyFAi6E/​edit|aici]] 
 </​hidden>​ </​hidden>​
-===== Exerciţii de Laborator ===== 
- 
-  - **[2p]** Să se scrie un program care citeşte de la tastatură un număr pozitiv n împreună cu alt număr pozitiv max. Programul va aloca apoi dinamic un vector de întregi de n elemente, pe care îl va iniţializa cu numere aleatoare în intervalul [0..max-1]. Sortaţi vectorul, folosind metoda preferată, afişându-i conţinutul atât înainte, cât şi după ce sortarea a avut loc. 
-  - **[3p]** Să se scrie un program care citeşte de la tastatură două matrice: una inferior triunghiulară (toate elementele de deasupra diagonalei principale sunt nule), şi cealaltă superior triunghiulară. Ele vor fi reprezentate în memorie cât mai compact cu putinţă (fară a stoca şi zerourile de deasupra, respectiv dedesubtul diagonalei). Se va calcula apoi produsul celor matrice, şi se va afişa. 
-  - Un număr lung (cu o valoare mult mai mare decât maximul reprezentabil pe un tip de date întreg standard din C), poate fi reprezentat ca un vector char *v de cifre (considerate valori de tip char), în felul următor: 
-      * v[0] reprezintă numărul de cifre ale numărului lung. Lungimea vectorului în memorie va fi v[0]+1. 
-      * v[i], unde i este de la 1 la v[0], reprezintă a i-a cifră a numărului, în ordinea crescătoare a semnificativităţii. Astfel v[1] reprezintă cifra unităţilor,​ v[2] cifra zecilor, etc. O reprezentare eficientă va avea întotdeauna ultima cifră v[v[0]] nenulă (altfel numărul de cifre ar fi putut fi mai mic şi reprezentarea mai compactă). 
-      * **[1p]** a) Scrieţi o funcţie care construieşte vectorul de cifre asociat unui număr întreg simplu (de tipul int):<​code>​char *build_number(int value);</​code>​ 
-      * **[2p]** b) Scrieţi o funcţie care adună două numere lungi şi întoarce ca rezultat un alt număr lung:<​code>​char *add_numbers(char *a, char *b);</​code>​ 
-      * **[2p]** c) Scrieţi un program care calculează şirul Fibonacci folosind numere lungi. Se cer primii 100 de termeni ai şirului, afişaţi pe câte o linie în parte. ​ 
-<​note>​Toate funcţiile cerute vor aloca dinamic memoria necesară reprezentării vectorului întors. Numerele nefolosite vor trebui eliberate, pentru a evita consumarea memoriei. Trataţi tipul de date char ca pe un tip numeric (deci lucraţi cu vectori de numere, nu cu şiruri de caractere ASCII).</​note>​ 
  
-==== Bonus ==== +[[https://drive.google.com/​open?​id=1xohyDQGMxXdGMvzyega5wKBi9WWk6qXW|Probleme laborator 14-16]]
-  - Considerând structura unui număr lung prezentată la punctul precedent, să se rezolve următoarele:​ +
-      * **[1p]** a) Scrieţi o funcţie care înmulţeşte două numere lungi şi întoarce ca rezultat un alt număr lung:<​code>​char *multiply_numbers(char *a, char *b);</code> +
-      * **[1p]** b) Scrieţi un program care calculează factorialul numerelor de la 1 la 50, afişând câte un număr pe fiecare linie. +
-  ​**[2p]** Se consideră un paralelipiped tridimensional cu dimensiunile citite de la tastatură, pentru care va trebui să alocaţi memorie. De asemenea, se citeşte apoi un număr pozitiv N, ce reprezintă un număr de bombe care vor fi plasate în paralelipiped. Apoi se citesc N triplete ce reprezintă coordonatele bombelor. Valorile citite vor trebui validate astfel încât să nu depăşească dimensiunile paralelipipedului. Pentru fiecare cub liber se va calcula numărul de bombe din cei maxim 26 de vecini ai săi, şi aceste numere vor fi afişate pe ecran, alături de coordonatele corespunzătoare. La sfârşitul execuţiei programului,​ memoria alocată va trebui eliberată.+
programare/laboratoare/lab08.1479899482.txt.gz · Last modified: 2016/11/23 13:11 by darius.neatu
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