130 likes | 266 Views
Programare in limbajul C – Cursul 12 Echivalenţa dintre pointeri şi tablouri Prof. univ. dr. Constantin Popescu. Agenda. S imilarităţi între tablouri şi pointeri Pointeri şi tablouri Alocarea memoriei Alocarea si e liberarea memoriei Re-alocarea blocurilor de memorie.
E N D
Programare in limbajul C – Cursul 12Echivalenţa dintre pointeri şi tablouriProf. univ. dr. Constantin Popescu
Agenda • Similarităţi între tablouri şi pointeri • Pointeri şi tablouri • Alocarea memoriei • Alocarea si eliberarea memoriei • Re-alocarea blocurilor de memorie
Similarităţi între tablouri şi pointeri • Există o serie de similarităţi în C între tablouri şi pointeri. Dacă avem un tablou int a[10]; • ne putem referi la a[i], a[1], a[2] etc. unde i este o variabilă de tip int. Dacă declarăm o variabilă pointer ip şi o facem să pointeze la începutul tabloului: int *ip = &a[0]; • ne putem referi la *ip, *(ip+1), *(ip+2) etc. sau la *(ip+i) unde i este de tip int. • Nu se poate atribui un tablou altuia; urmatoarea secvenţa de codnu este permisă: int a[10], b[10]; a = b; /* GREŞIT */ • În schimb asignarea unui pointer altuia este permisă: int *ip1, *ip2; ip1 = &a[0]; ip2 = ip1;
Similarităţi între tablouri şi pointeri • Dacă scriem: ip[3] • este ca şi cum am fi scris: *(ip + 3) • De exemplu, dacă tabloul de caractere char string[100]; • conţine un şir de caractere, pentru a afla lungimea acestui şir putem folosi funcţia: int mystrlen(char *string) { int len; char *p; for(p = string; *p != '\0'; p++) ; /* După terminarea instrucţiunii for, p pointează la caracterul '\0' */ len = p - string; /* Expresia p-string este echiv. cu p-&string[0] */ return len; }
Tablouri şi pointeri ca argumente ale funcţiilor #include <stddef.h> #include <ctype.h> int getwords(char *line, char *words[], int maxwords){ char *p = line; int nwords = 0; while(1){ while(isspace(*p)) p++; if(*p == '\0')return nwords; words[nwords++] = p; while(!isspace(*p) && *p != '\0') p++; if(*p == '\0')return nwords; *p++ = '\0'; if(nwords >= maxwords) return nwords; } } • Mai jos dăm o secvenţă completă de apel a funcţiei getwords: ... char line[]="acesta este untest"; int i; nwords=getwords(line,words,10); for(i = 0; i < nwords; i++) printf("%s\n", words[i]); ...
Alocarea memoriei • Când alocăm memorie dinamic folosind malloc trebuie să manifestăm multă precauţie întrucât aceasta este o funcţie de "nivel jos". • Problema este că dacă atribuim o valoare unei locaţii pointate de un pointer, de ex.: *p = 0; • şi dacă pointerul p nu pointează "nicăieri" (de fapt el va pointa ceva dar nu ceea ce vrem noi), zeroul este scris în locul acelui "ceva". • Dacă acel "ceva" este o zonă de memorie utilizată de program sau, mai rău, dacă sistemul de operare utilizează acel "ceva" şi sistemul de operare nu are protecţie împotriva unor astfel de instrucţiuni, atunci putem asista la un comportament "ciudat" din partea sistemului pe care îl folosim, putând ajunge până la imposibilitatea de a-l utiliza şi necesitatea repornirii acestuia.
Alocarea memoriei cu malloc (1) • Iată un prim exemplu: #include <stdlib.h> . . . char *line; int linelen = 100; line = malloc(linelen); /* incomplet -- valoare de return a functiei malloc nu este testata*/ getline(line, linelen); • Funcţia malloc este declarată în fişierul stdlib.h, aşa că acest fişier header trebuie inclus în orice program care apelează funcţia malloc. • Un "octet" este în C, prin definiţie, o cantitate de memorie necesară pentru stocarea unui caracter • Apelul funcţiei malloc de mai sus ne va da exact atâtea caractere câte am cerut.
Alocarea memoriei cu malloc (2) • Putem ilustra pointerul care rezultă prin: • Ca un al doilea exemplu, am putea fi puşi în situaţia de a aloca o zonă de memorie şi să copiem un şir de caractere în acea zonă de memorie cu funcţia strcpy: char *p = malloc(15); /* incomplet - valoare de return a functiei malloc nu este testata */ strcpy(p, "Hello, world!"); • Când copiem şiruri de caractere e important să ţinem cont de faptul că acestea se termină cu caracterul '\0'.
Dacă folosim funcţia strlen pentru a număra caracterele dintr-un şir, numărul nu va conţine şi caracterul '\0', aşa că va trebui să-l adăugăm noi înainte de apela funcţia malloc: char *unstring, *copy; ... copy = malloc(strlen(unstring) + 1); /* +1 pentru \0 */ /* incomplet - valoare de return a functiei malloc nu este testata */ strcpy(copy, unstring); Pentru a aloca spaţiu pentru 100 de întregi, putem scrie: int *ip = malloc(100 * sizeof(int)); Utilizarea operatorului sizeof seamănă cu un apel de funcţie, dar în realitate el este un operator, care îşi face datoria în timpul compilării. După apelul de mai sus al funcţiei malloc, ip este iniţializat pentru a pointa la o zonă de memorie ce poate stoca 100 de întregi, ce pot fi accesaţi prin ip[0], ip[1], ip[2], … , ip[99]. Alocarea memoriei cu malloc (3)
Alocarea si eliberarea memoriei • Un apel la malloc, cu verificarea erorilor, se scrie de obicei sub forma: int *ip = malloc(100 * sizeof(int)); if(ip == NULL) { printf("memoria insuficienta\n"); exit sau return } • După afişarea mesajelor de eroare, codul de mai sus trebuie să redea controlul funcţiei apelante sau să oprească execuţia programului. • Dacă p conţine un pointer la o zonă de memorie obţinută prin apelul funcţiei malloc, aceasta poate fi eliberată prin apelul: free(p); • După apelul free(p); • Pointerul p pointează la o anumită zonă de memorie.
Exemplu alocare-dealocare memorie /* Functia care determina toate numerele prime ce sunt mai mici decit m */ void print_prime( int m ) { int i,j; char * ary = malloc( m ); if (ary == NULL) return -1; for( i = 0; i < m; i++ ) ary[i]=1; ary[0] = ary[1] = 0; ary[2] = 1; for( i = 3;i < m; i++ ) { for( j = 2; j < i; j++) if(ary[ j ] && i%j == 0){ ary[j] = 0; break; } } for(i = 0; i < m; i++) if( ary[i] ) printf(“%d “, i); free( ary ); }
Re-alocarea blocurilor de memorie (1) • Dacă am alocat memorie pentru 100 de elemente şi utilizatorul a introdus 101 ar fi de dorit să putem reţine cele 100 de elemente şi să alocăm memorie suplimentară pentru încă 100. • Aceasta se poate face cu funcţia realloc. • Funcţia realloc primeşte vechiul pointer (cel obţinut pin apelul iniţial al funcţiei malloc) şi noua dimensiune şi va face tot posibilul pt. a returna o zonă de memorie suficient de mare pentru a satisface noile cerinţe. • De exemplu, dacă vrem ca variabila ip din exemplul precedent să pointeze la 200 de întregi în loc de 100, putem apela funcţia: ip = realloc(ip, 200 * sizeof(int)); • Putem folosi o secvenţă ca cea de mai jos: int *newp; newp = realloc(ip, 200 * sizeof(int)); if(newp != NULL) ip = newp; else { printf("memorie insuficienta\n"); /* exit sau return */ /* ip pointeaza in continuare la 100 de intregi */ }
Re-alocarea blocurilor de memorie (2) • Secvenţa de cod care citeşte linii de text de la utilizator, fiecare linie va fi interpretată ca un număr prin apelarea funcţiei atoi, şi întregii vor fi stocaţi într-un "tablou" alocat dinamic. #define MAXLINE 100 char line[MAXLINE]; int *ip; int nalloc, nitems; nalloc = 100; ip = malloc(nalloc * sizeof(int)); /* alocarea initiala */ if(ip == NULL){ printf("memorie insuficienta\n"); exit(1); } nitems = 0; while(getline(line, MAXLINE) != EOF){ if(nitems >= nalloc) {/* facem o realocare */ int *newp; nalloc += 100; newp = realloc(ip, nalloc * sizeof(int)); if(newp == NULL) { printf("memorie insuf.\n"); exit(1); } ip = newp; } ip[nitems++] = atoi(line); }