Beruflich Dokumente
Kultur Dokumente
-1-
INFORMATIC*I*
}
printf(a = %d\n, a);
return 0;
}
Coninut stiv:
(x) 2
(b) 5
(a) 10
......
}
int f2() {
static int y = 99; /*Variabil local static, iniializat cu 99
doar la primul apel al lui f2; valoarea ei este
reinut pe parcursul apelurilor lui f2*/
......
}
int f() {
static int nr_apeluri=0;
nr_apeluri++;
printf("funcia f() este apelata pentru a %d-a oara\n, nr_apeluri);
return nr_apeluri;
}
int main() {
int i;
for (i=0; i<10; i++) f();
//f() apelata de 10 ori
printf("functia f() a fost apelata de %d ori.", f()); // 11 ori!!
return 0;
}
Observaii:
Variabilele locale statice se folosesc foarte rar n practica programrii ( funcia de bibliotec strtok
este un exemplu de funcie cu o variabil static).
Variabilele statice pot fi iniializate numai cu valori constante (pentru c iniializarea are loc la
compilare), dar variabilele auto pot fi iniializate cu rezultatul unor expresii (pentru c iniializarea are
loc la execuie).
Exemplu de funcie care afieaz un ntreg pozitiv n cod binar, folosind cturile mpririi cu
puteri descresctoare ale lui 10:
-2-
INFORMATIC*I*
Toate variabilele externe (i statice) sunt automat iniializate cu valori zero (inclusiv vectorii).
Cuvntul cheie static face ca o variabil global sau o funcie s fie privat(proprie) unitii unde a
fost definit: ea devine inaccesibil altei uniti, chiar prin folosirea lui extern.
Cantitatea de memorie alocat pentru variabilele cu nume rezult din tipul variabilei i din
dimensiunea declarat pentru vectori. Memoria alocat dinamic este specificat explicit ca parametru
al funciilor de alocare, n numr de octei.
Memoria neocupat de datele statice i de instruciunile unui program este mprit ntre stiv i
heap.
Consumul de memorie stack (stiva) este mai mare n programele cu funcii recursive (numr mare
de apeluri recursive).
Consumul de memorie heap este mare n programele cu vectori i matrice alocate (i realocate)
dinamic.
De observat c nu orice vector cu dimensiune constant este un vector static; un vector definit ntr-o
funcie (alta dect main) nu este static deoarece nu ocup memorie pe toat durata de execuie a
programului, dei dimensiunea sa este stabilit la scrierea programului. Un vector definit ntr-o
funcie este alocat pe stiv, la activarea funciei, iar memoria ocupat de vector este eliberat
automat la terminarea funciei.
Sintez variabile locale / variabile globale
O sintez legat de variabilele locale i cele globale din punct de vedere al duratai de via vs.
domeniu de vizibilitate este dat n tabelul urmtor:
Variabile globale
Variabile locale
Alocare
Static; la compilare
Durata de via
Cu zero
Nu se face automat
Iniializare
O variabil declarat register solicit sistemului alocarea ei ntr-un registru main, dac este
posibil.
De obicei compilatorul ia automat decizia de alocare a registrelor mainii pentru anumite variabile
auto din funcii. Se utilizeaz pentru variabile foarte solicitate, pentru mrirea vitezei de execuie.
Exemplu:
{
register int i;
for(i = 0; i < N; ++i){
-3-
INFORMATIC*I*
/* */
}
} /* se elibereaza registrul */
// File2.cpp
int i = 88;
// Definit aici
Dac cererea de alocare nu poate fi satisfcut, pentru c nu mai exista un bloc continuu de
dimensiunea solicitat, atunci funciile de alocare au rezultat NULL. Funciile de alocare au rezultat
void* deoarece funcia nu tie tipul datelor ce vor fi memorate la adresa respectiv.
La apelarea funciilor de alocare se folosesc:
Operatorul sizeof pentru a determina numrul de octei necesar unui tip de date
(variabile);
Operatorul de conversie cast pentru adaptarea adresei primite de la funcie la tipul
datelor memorate la adresa respectiv (conversie necesar atribuirii ntre pointeri de
tipuri diferite).
-4-
INFORMATIC*I*
Exemple:
//aloca memorie pentru 30 de caractere:
char * str = (char*) malloc(30);
//aloca memorie ptr. n ntregi:
int * a = (int *) malloc( n * sizeof(int));
//aloca memorie ptr. n ntregi si initializeaza cu zerouri
int * a= (int*) calloc (n, sizeof(int) );
Atenie! Se va evita redimensionarea unui vector cu o valoare foarte mic de un numr mare de ori;
o strategie de realocare folosit pentru vectori este dublarea capacitii lor anterioare.
Exemplu de funcie cu efectul funciei realloc, dar doar pentru caractere:
char * ralloc (char * p, int size) {
char *q;
if (size==0) {
free(p);
return NULL;
}
q = (char*) malloc(size);
if (q) {
memcpy(q,p,size);
free(p);
}
return q;
}
// p = adresa veche
// q=adresa noua
// echivalent cu free
//
//
//
//
aloca memorie
daca alocare reusita
copiere date de la p la q
elibereaza adresa p
// q poate fi NULL
Observaie: La mrirea blocului, coninutul zonei alocate n plus nu este precizat, iar la micorarea
blocului se pierd datele din zona la care se renun.
IB.10.6. 3 Eliberarea memoriei
Funcia free are ca argument o adres (un pointer) i elibereaz zona de la adresa respectiv (alocat
dinamic). Dimensiunea zonei nu mai trebuie specificat deoarece este memorat la nceputul zonei
alocate (de ctre funcia de alocare):
void free(void* adr);
Eliberarea memoriei prin free este inutil la terminarea unui program, deoarece nainte de
ncrcarea i lansarea n execuie a unui nou program se elibereaz automat toat memoria heap.
Exemple:
char *str;
-5-
INFORMATIC*I*
str=(char *)malloc(10*sizeof(char));
str=(char *)realloc(str,20*sizeof(char));
free(str);
Observaie:
Atenie la definirea de iruri n mod dinamic! irul respectiv trebuie iniializat cu adresa unui alt ir sau a
unui spaiu alocat pe heap (adic alocat dinamic)!
Exemple:
char *sir3;
char ir1[30];
// Varianta 1: sir3 ia adresa unui ir static
sir3 = sir1;
// Echivalent cu: sir3=&sir1; sir3=&sir1[0];
char *sir4="test"; //sir4 este iniializat cu adresa unui ir constant
// Varianta 2: se aloc dinamic un spaiu pe heap
sir3=(char *)malloc(100*sizeof(char));
Exemplu
Program care aloc spaiu pentru o variabil ntreag dinamic, dup citire i tiprire, spaiul fiind
eliberat. Modificai programul astfel nct variabila dinamic s fie de tip double.
Rezolvare
#include <stdlib.h>
#include <stdio.h>
int main(){
int *pi;
pi=(int *)malloc(sizeof(int));
if(pi==NULL){
puts("*** Memorie insuficienta ***");
return 1;
// revenire din main
}
printf("valoare:");
//citirea variabilei dinamice, de pe heap, de la adresa din pi!!!
scanf("%d",pi);
*pi=*pi*2;
// dublarea valorii
printf("val=%d,pi(adresa pe heap)=%p,adr_pi=%p\n", *pi, pi, &pi);
// sizeof aplicat unor expresii:
printf("%d %d %d\n",sizeof(*pi), sizeof(pi), sizeof(&pi));
free(pi);
//eliberare spatiu
printf("pi(dupa elib):%p\n",pi); // nemodificat, dar invalid!
return 0;
}
-6-
INFORMATIC*I*
Dezavantajul unui vector cu dimensiune fix (stabilit la declararea vectorului i care nu mai poate
fi modificat la execuie) apare n aplicaiile cu vectori de dimensiuni foarte variabile, n care este
dificil de estimat o dimensiune maxim, fr a face risip de memorie.
De cele mai multe ori programele pot afla din datele citite dimensiunile vectorilor cu care lucreaz
i deci pot face o alocare dinamic a memoriei pentru aceti vectori. Aceasta este o soluie mai
flexibil, care folosete mai bine memoria disponibil i nu impune limitri arbitrare asupra
utilizrii 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 si mai mult utilizarea unor vectori cu
dimensiune variabil.
Un vector alocat dinamic se declar ca variabil pointer care se iniializeaz cu rezultatul funciei de
alocare. Tipul variabilei pointer este determinat de tipul componentelor vectorului.
Exemplu:
#include <stdlib.h>
#include <stdio.h>
int main() {
int n, i;
int * a;
// adresa vector alocat dinamic
printf ("n=");
scanf ("%d", &n);
// dimensiune vector
a=(int *) calloc (n,sizeof(int));
// aloca memorie pentru vector
// sau: a=(int*) malloc (n*sizeof(int));
// citire component vector:
printf ("componente vector: \n");
for (i=0;i<n;i++)
scanf ("%d", &a[i]);
// afisare vector:
for (i=0;i<n;i++)
printf ("%d ",a[i]);
return 0;
// sau
Exist i cazuri n care datele memorate ntr-un vector rezult din anumite prelucrri, iar numrul
lor nu poate fi cunoscut de la nceputul execuiei. n acest caz se poate recurge la o realocare
dinamic a memoriei. O strategie de realocare pentru vectori este dublarea capacitii lor anterioare.
n exemplul urmtor se citete un numr necunoscut de valori ntregi ntr-un vector extensibil:
Program care citete numere reale pn la CTRL+Z, le memoreaz ntr-un vector alocat i realocat
dinamic n funcie de necesiti i le afieaz.
Rezolvare:
#include <stdio.h>
#include <stdlib.h>
#define INCR 4
int main() {
int n,n_crt,i ;
float x, * v;
n = INCR;
n_crt = 0;
-7-
INFORMATIC*I*
//alocare initiala
Din exemplele anterioare lipsete eliberarea memoriei alocate pentru vectori, dar fiind vorba de un
singur vector alocat n funcia main i necesar pe toat durata de execuie, o eliberare final este
inutil. Eliberarea explicit poate fi necesar pentru vectori de lucru, alocai dinamic n funcii.
IB.10.8. Matrice alocate dinamic
Alocarea dinamic pentru o matrice este important deoarece folosete economic memoria i
permite matrice cu linii de lungimi diferite. De asemenea reprezint o soluie bun la problema
parametrilor de funcii de tip matrice.
O matrice alocat dinamic este de fapt un vector de pointeri ctre fiecare linie din matrice, deci un
vector de pointeri la vectori alocai dinamic. Dac numrul de linii este cunoscut sau poate fi
estimat valoarea lui maxim, atunci vectorul de pointeri are o dimensiune constant. O astfel de
matrice se poate folosi la fel ca o matrice declarat cu dimensiuni constante.
Exemplu de declarare matrice de ntregi:
int * a[M];
// M este o constanta simbolica
Dac nu se poate estima numrul de linii din matrice atunci i vectorul de pointeri se aloc dinamic,
iar declararea matricei se face ca pointer la pointer:
int** a;
n acest caz se va aloca mai nti memorie pentru un vector de pointeri (funcie de numrul liniilor)
i apoi se va aloca memorie pentru fiecare linie cu memorarea adreselor liniilor n vectorul de
pointeri.
Notaia a[i][j] este interpretat astfel pentru o matrice alocat dinamic:
a[i] conine un pointer (o adres b)
b[j] sau b+j conine ntregul din poziia j a vectorului cu adresa b.
Exemplu
S se scrie funcii de alocare a memoriei i afiare a elementelor unei matrice de ntregi alocat
dinamic.
#include<stdio.h>
#include<stdlib.h>
// rezultat adresa matrice sau NULL
int ** intmat ( int nl, int nc) {
int i;
int ** p=(int **) malloc (nl*sizeof (int*));
if ( p != NULL)
for (i=0; i<nl ;i++)
p[i] =(int*) calloc (nc,sizeof (int));
-8-
INFORMATIC*I*
return p;
}
void printmat (int ** a, int nl, int nc) {
int i,j;
for (i=0;i<nl;i++) {
for (j=0;j<nc;j++)
printf (%2d, a[i][j] );
printf(\n);
}
}
int
main () {
int **a, nl, nc, i, j;
printf ("nr linii i nr coloane: \n");
scanf ("%d%d", &nl, &nc);
a= intmat(nl,nc);
for (i=0;i<nl;i++)
for (j=0;j<nc;j++)
a[i][j]= nc*i+j+1;
printmat (a ,nl,nc);
return 0;
Funcia printmat dat anterior nu poate fi folosit pentru afiarea unei matrice cu dimensiuni
constante. Explicaia este interpretarea diferit a coninutului zonei de la adresa aflat n primul
argument.
Astfel, chiar dac exemplul urmtor este corect sintactic el nu se execut corect:
int x [2][2]={{1,2},{3,4}};
printmat ( (int**)x, 2, 2);
// 2 linii i 2 coloane
-9-
INFORMATIC*I*
Atenie! Acest pointer nu trebuie s conin adresa unei variabile locale, deoarece:
O variabil local are o existen temporar, garantat numai pe durata executrii funciei n care
este definit (cu excepia variabilelor locale statice)
Adresa unei astfel de variabile nu trebuie transmis n afara funciei, pentru a fi folosit ulterior!!
Exemplu greit:
// vector cu cifrele unui nr intreg de maxim cinci cifre
int * cifre (int n) {
int k, c[5];
// vector local
for (k=4;k>=0;k--) {
c[k]=n%10; n=n/10;
}
return c;
// aici este eroarea !
}
//warning la compilare i POSIBIL rezultate greite n main!!
O funcie care trebuie s transmit ca rezultat un vector poate fi scris corect n n mai multe feluri:
1. Primete ca parametru adresa vectorului (definit i alocat n alt funcie) i depune
rezultatele la adresa primit (este soluia recomandat!!)
void cifre (int n, int c[ ]) {
int k;
for (k=4;k>=0;k--) {
c[k]=n%10; n=n/10;
}
}
int main(){
int a[10];
.
cifre(n,a);
.
}
3. O soluie oarecum echivalent este utilizarea unui vector local static, care continu s existe i
dup terminarea funciei.
- 10 -
INFORMATIC*I*
dinamica
ptr
De reinut c trebuie create adrese distincte pentru fiecare variabil structur i c ar fi greit s
punem adresa variabilei d n toate poziiile din vector!
Este posibil i varianta urmtoare pentru ciclul principal din main dac cunoatem numrul de
elemente din structur:
....
scanf (%d,&n);
// numar de structuri ce vor fi citite
for (k=0; k<n; k++) {
dp = (date*) malloc (sizeof(date));// alocare dinamica ptr structur
scanf ("%d%d%d", &dp->zi, &dp->luna, &dp->an)
vp[n++]=dp;
// memoreaza adresa in vector
}
....
Exemplu
Program pentru citirea unor nume, alocare dinamic a memoriei pentru fiecare ir (n funcie de
lungimea irului citit) i memorarea adreselor irurilor ntr-un vector de pointeri. n final se vor
afia numele citite, pe baza vectorului de pointeri.
- 11 -
INFORMATIC*I*
la
iruri
prin
Bubble
Sort
while(schimb){
schimb=0;
for (i=0;i<n-1;i++)
if ( strcmp (vp[i],vp[i+1])>0) {
tmp = vp[i];
vp[i] = vp[i+1];
vp[i+1] = tmp;
schimb = 1;
}
}
}
int main () {
int n;
char * vp[1000];
n=readstr(vp);
sort(vp,n);
printstr(vp,n);
return 0;
- 12 -
(metoda
INFORMATIC*I*
// camp de date
// camp de legatura
Programul urmtor arat cum se poate crea i afisa o list cu adugare la nceput (o stiv realizat ca list
nlnuit):
int main ( ) {
- 13 -
INFORMATIC*I*
Cmpul de date poate fi la rndul lui o structur specific aplicaiei sau poate fi un pointer la date
alocate dinamic (un ir de caractere, de exemplu).
De obicei se definesc funcii pentru operaiile uzuale cu liste.
Exemple:
typedef struct nod {
int val;
struct snod *leg;
} nod;
Alte structuri dinamice folosesc cte doi pointeri sau chiar un vector de pointeri; ntr-un arbore
binar fiecare nod conine adresa succesorului la stnga i adresa succesorului la dreapta, ntr-un
arbore multici fiecare nod conine un vector de pointeri ctre succesorii acelui nod.
- 14 -
INFORMATIC*I*
Operatorul new are o form puin modificat la alocarea de memorie pentru vectori, pentru a
specifica numrul de componente.
Exemplu:
int * v = new int [n];
// vector de n intregi
Operatorul delete are ca operand o variabil pointer i are ca efect eliberarea blocului de memorie
adresat de pointer, a crui mrime rezult din tipul variabilei pointer sau este indicat explicit.
Exemple:
int * v;
delete v;
delete [ ] v;
delete [n] v;
// alocare memorie
// eliberare memorie
Dup alocarea de memorie cu new se pot folosi funciile realloc i free pentru realocare sau
eliberare de memorie.
- 15 -