Diferență între revizuiri ale paginii „PC Laborator 12”
Linia 101: | Linia 101: | ||
* <code>void * calloc(unsigned elements, unsigned elementSize)</code> - alocă ''elements'' elemente de ''elementSize'' octeți fiecare într-o zonă continuă din HEAP și întoarce adresa de memorie unde începe zona respectivă; dacă alocarea eșuează (nu exită suficientă memorie într-o zonă continuă în HEAP), funcția va întoarce '''NULL'''; alocarea șterge tot conținutul memoriei respective, scriind 0 la fiecare locație; | * <code>void * calloc(unsigned elements, unsigned elementSize)</code> - alocă ''elements'' elemente de ''elementSize'' octeți fiecare într-o zonă continuă din HEAP și întoarce adresa de memorie unde începe zona respectivă; dacă alocarea eșuează (nu exită suficientă memorie într-o zonă continuă în HEAP), funcția va întoarce '''NULL'''; alocarea șterge tot conținutul memoriei respective, scriind 0 la fiecare locație; | ||
* <code>void free (void *)</code> - dezalocă o zonă de memorie alocată în prealabil cu <code>malloc</code> sau <code>calloc</code>; apelul succesiv de două sau mai multe ori a funcției <code>free</code> pentru aceeași zonă de memorie sau apelul ei pentru o adresă care nu a fost alocată în prealabil va avea ca efect oprirea imediată a programului cu eroare (''double free or corruption''). | * <code>void free (void *)</code> - dezalocă o zonă de memorie alocată în prealabil cu <code>malloc</code> sau <code>calloc</code>; apelul succesiv de două sau mai multe ori a funcției <code>free</code> pentru aceeași zonă de memorie sau apelul ei pentru o adresă care nu a fost alocată în prealabil va avea ca efect oprirea imediată a programului cu eroare (''double free or corruption''). | ||
+ | |||
+ | Toate aceste funcții sunt definite în fișierul header <code style="color: green">stdlib.h</code>. | ||
+ | |||
+ | <syntaxhighlight lang="c"> | ||
+ | #include <stdlib.h> | ||
+ | #include <stdio.h> | ||
+ | |||
+ | int main(){ | ||
+ | int * intPointer; | ||
+ | short * shortPointer; | ||
+ | |||
+ | intPointer = (int*) malloc(sizeof(int)); | ||
+ | shortPointer = (short*) calloc(1, sizeof(short)); | ||
+ | |||
+ | printf("Valoarea din zona alocata pentru int este: %d\n", *intPointer); | ||
+ | printf("Valoarea din zona alocata pentru short este: %hd\n", *shortPointer); | ||
+ | |||
+ | free(intPointer); | ||
+ | free(shortPointer); | ||
+ | |||
+ | return 0; | ||
+ | } | ||
+ | </syntaxhighlight> |
Versiunea de la data 30 decembrie 2015 19:39
Obiective
La sfârșitul acestui laborator studenții vor fi capabili:
- să definească și să utilizeze tipuri de date pointer;
- să folosească pointeri pentru a putea modifica variabilele trimise ca argumente unor funcții;
- să aloce, să folosească și să elibereze memorie HEAP, în mod dinamic;
- să utilizeze aritmetica pointerilor pentru a itera peste elemente de la adrese consecutive de memorie;
- să utilizeze
valgrind
pentru a diagnostica pierderile de memorie.
Tipuri de date pointer
Un pointer reprezintă o variabilă care stochează o adresă în memoria dedicată aplicației. Tipul variabilei de tip pointer specifică tipul datei care poate fi citit de la adresa respectivă.
O variabilă de tip pointer se definește în felul următor:
<tip_data> * <nume_variabila>;
Spre exemplu:
int * pa;
Variabila de tip pointer pa
nu memorează un întreg, ci o adresă în memorie, iar de la adresa respectivă se poate citi un întreg. Acest lucru se numește indirectare simplă. În plus, deoarece tipul pointer este în sine un tip de dată, și în definiția unui pointer <tip_data> poate fi un pointer, aceasta permite următoarele construcții:
int * pa;
- variabilă ce stochează o adresă de unde se poate citi un întreg (indirectare simplă);int ** pa;
- variabilă ce stochează o adresă de unde se poate citi o adresă de unde se poate citi un întreg (dublă indirectare);int *** pa;
- variabilă ce stochează o adresă de unde se poate citi o adresă de unde se poate citi o adresă de unde se poate citi un întreg (triplă indirectare);- etc.
În C există un tip de dată pointer care poate memora o adresă fără a știi ce date se află la adresa respectivă. Acest tip de pointer este void*
.
Dimensiunea tipului de date pointer
Conform regulii de mai sus, și variabilele de tip pointer ocupă loc în memorie, deci au dimensiune, în octeți. Un pointer nu oferă informații legate de dimensiunea ocupată de datele de la adresa respectivă, ci doar adresa de unde începe zona ocupată. Din moment ce un pointer memorează doar o adresă, dimensiunea variabilelor de tip pointer nu depinde decât de dimensiunea spațiului de memorie. Astfel, pentru procesoare și sisteme de operare pe 32 de biți, o variabilă de tip pointer va avea 32 de biți, iar pe procesoare și sisteme de operare pe 64 de biți, o variabilă de tip pointer va avea 64 de biți.
Adresa unei variabile
Operatorul care permite aflarea adresei unde este stocată o variabilă este ampersand (&). Acesta este un operator unar ce se plasează înaintea unei variabile iar rezultatul evaluării sale este adresa unde este stocată variabila respectivă. Această adresă poate fi stocată într-o altă variabilă de tipul corespunzător. Altfel spus, pentru o variabilă de tip tip_data, adresa acesteia se poate stoca într-o variabilă de tip tip_data *:
float floatValue;
float * floatAddress = &floatValue;
int intValue;
int * intAddress = &intValue;
int ** intPointerAddress = &intAddress;
Adresa NULL
Pentru orice aplicație, adresa 0 din spațiul ei de memorie este rezervată. Aceasta nu poate fi nici scrisă și nici citită. Această adresa poartă numele de NULL. Orice variabilă de tip pointer poate lua valoarea NULL, lucru care de obicei specifică faptul că de fapt variabila pointer nu stochează o adresă validă.
Constanta NULL este definită în fișierul header stdlib.h
(Standard Library).
#include <stdlib.h>
int main() {
char * charPointer = NULL;
return 0;
}
Valoarea de la o adresă
Având o variabilă de tip pointer, valoarea stocată în memorie la adresa respectivă se poate afla folosind caracterul steluță (*), numit și operator de indirectare. Acesta este un operator unar ce se plasează înaintea unei variabile de tip pointer iar rezultatul evaluării sale este valoarea din memorie de la adresa stocată în variabila respectivă. Această valoare poate fi stocată într-o altă variabilă de tipul corespunzător. Altfel spus, pentru o variabilă pointer de tip tip_data *, valoarea de la adresa stocată de pointerul respectiv se poate memora într-o altă variabilă de tip tip_data:
float floatValue;
float * floatAddress = &floatValue;
float anotherFloatValue = *floatAddress;
int intValue;
int * intAddress = &intValue;
int ** intPointerAddress = &intAddress;
int * anotherintAddress = *intPointerAddress;
Utilitatea variabilelor de tip pointer
Pointerii în C au două roluri foarte importante:
- Alocarea dinamică de memorie în HEAP
- Modificarea variabilelor parametri ale unor funcții astfel încât modificarea să se păstreze în afara funcției
Alocarea dinamică de memorie
Memoria alocată unei aplicații de către sistemul de operare este împărțită în mai multe secțiuni, din care importante pentru stocarea de date sunt:
- Segmentele BSS și Data - reprezintă memoria alocată pentru variabilele statice (globale), care există de la începutul până la încheierea programului, fără posibilitate de eliberare;
- Stiva (Stack) - zona de memorie în care se alocă contextele funcțiilor apelate în timpul execuției programului și în care se alocă argumentele și variabilele locale are funcțiilor; această zonă este alocată la intrarea în funcție și este eliberată la ieșirea din funcție;
- HEAP - zonă de memorie în care se pot aloca dinamic, de către programator, blocuri de memorie ce pot fi folosite în program până la eliberarea acestora de către programator.
Alocarea și dezalocarea memoriei în heap se face folosind următoarele funcții:
void * malloc(unsigned size)
- alocă size octeți într-o zonă continuă din HEAP și întoarce adresa de memorie unde începe zona respectivă; dacă alocarea eșuează (nu exită suficientă memorie într-o zonă continuă în HEAP), funcția va întoarce NULL; alocarea nu șterge conținutul memoriei respective;void * calloc(unsigned elements, unsigned elementSize)
- alocă elements elemente de elementSize octeți fiecare într-o zonă continuă din HEAP și întoarce adresa de memorie unde începe zona respectivă; dacă alocarea eșuează (nu exită suficientă memorie într-o zonă continuă în HEAP), funcția va întoarce NULL; alocarea șterge tot conținutul memoriei respective, scriind 0 la fiecare locație;void free (void *)
- dezalocă o zonă de memorie alocată în prealabil cumalloc
saucalloc
; apelul succesiv de două sau mai multe ori a funcțieifree
pentru aceeași zonă de memorie sau apelul ei pentru o adresă care nu a fost alocată în prealabil va avea ca efect oprirea imediată a programului cu eroare (double free or corruption).
Toate aceste funcții sunt definite în fișierul header stdlib.h
.
#include <stdlib.h>
#include <stdio.h>
int main(){
int * intPointer;
short * shortPointer;
intPointer = (int*) malloc(sizeof(int));
shortPointer = (short*) calloc(1, sizeof(short));
printf("Valoarea din zona alocata pentru int este: %d\n", *intPointer);
printf("Valoarea din zona alocata pentru short este: %hd\n", *shortPointer);
free(intPointer);
free(shortPointer);
return 0;
}