Transcript
  • 8. Tablouri i pointeri

    Att tablourile, ct i pointerii reprezint tipuri de date derivate. Tablourile sunt derivate din tipul

    de date al elementelor componente, iar pointerii sunt derivai din tipul de date al elementelor spre

    care indic. n plus, tablourile reprezint tipuri de date compuse, metoda de structurare a

    elementelor componente ntr-un tablou fiind realizat cu ajutorul indexrii. n mod uzual studiul

    celor dou tipuri de date se face mpreun, deoarece tablourile sunt tratate de ctre

    compilatoarele limbajului C ca nite pointeri constani, la care se adaug anumite metode de

    acces la elmentele componente.

    8.1 Tablouri

    Din punct de vedere al tipurilor de date, tipurile tablou sunt tipuri de date compuse, n sensul c

    un element de tip tablou este compus din mai multe elemente aparinnd altor tipuri de date,

    numite componentele tabloului. Toate componentele unui tablou trebuie s aparin ns

    aceluiai tip de date, numit tip de date de baz, ceea ce face ca tablourile s fie n acelai timp i

    tipuri de date derivate.

    Structurarea elementelor componente ale tablourilor se face cu ajutorul indicilor:

    tablourile cu un singur indice se numesc tablouri unidimensionale, cele cu doi indici sunt tablouri

    bidimensionale, iar n general tablourile cu mai muli indici sunt tablouri multidimensionale.

    Tablourile unidimensionale sunt tipurile fundamentale, deoarece tablourile multidimensionale se

    definesc pe baza celor unidimensionale.

    Deoarece tablourile sunt tipuri de date derivate, definirea sau declararea acestora se face

    prin declararea tipului de baz utiliznd operatorul de derivare ([]). n cazul tablourilor

    multidimensionale, operatorul [] se aplic pentru fiecare indice separat. De exemplu, secvena

    de program urmtoare: double x[5], d[3][4], z[2][4][3];

    definete tabloul unidimensional x cu 5 componente de tip double, tabloul bidimensional d ca

    un tablou unidimensional de 3 componente, fiecare component fiind un tablou unidimansional

    de 4 componente de tip double i tabloul tridimensional z ca fiind un tablou unidimensional

    de 2 componente care sunt tablouri bidimensionale de 43 componente de tip double.

    Observaie. Pentru fiecare indice trebuie specificat numrul de elemente asociat indicelui

    respectiv. Aceasta nseamn c n limbajul C nu sunt permise tablouri cu dimensiune variabil,

    cum sunt cele din limbajul Basic de exemplu. Alocarea memoriei pentru tablouri este static, n

    sensul c dimensiunile tablourilor se stabilesc la compilarea programului i nu n timpul

    execuiei acestuia.

  • Pentru referirea componentelor unui tablou se utilizeaz operatorul de indexare (reprezentat tot

    de grupul de caractere[]) conform sintaxei: []

    unde reprezint o expresie ntreg. O component a unui tablou reprezint o L-

    valoare deoarece are asociat o zon de memorie pentru pstrarea valorii curente.

    Observaie. n cazul tablourilor multiplu indexate, operatorul de indexare se specific pentru

    fiecare indice. De exemplu, pentru un tablou bidimensional cu numele a, elementul de pe linia i

    i coloana j se refer a[i][j], deoarece a[i] reprezint el nsui un tablou unidimensional.

    Dup cum s-a precizat anterior, indicii elementelor unui tablou sunt numere ntregi pozitive;

    indicele primului element este ntotdeauna zero, pe cnd indicele ultimului element este mai mic

    cu 1 dect numrul total de elemente pentru indicele respectiv. Dei compilatoarele limbajului C

    nu fac o verificare a valorilor pentru indicii tablourilor (i deci nu genereaz mesaje de eroare la

    compilare), utilizarea unui element dint-un tablou care are indicele n afara domeniului corect de

    valori poate conduce la erori n execuia unui program.

    Modul de alocare a memoriei pentru componentele unui tablou este liniar, indiferent de

    numrul de indici al acestuia: elementele tabloului se aloc n ordinea natural a creterii

    indicilor. De exemplu, alocarea memoriei pentru un tablou tridimensional definit astfel: int r[2][3][2];

    presupune urmtoarea ordine a componentelor: r[0][0][0], r[0][0][1], r[0][1][0], r[0][1][1], r[0][2][0],

    r[0][2][1], r[1][0][0], r[1][0][1], r[1][1][0], r[1][1][1],

    r[1][2][0], r[1][2][1].

    Datorit faptului c alocarea memoriei pentru elementele unui tablou se face liniar, n cazul

    expresiilor ce conin referire la componentele unui tablou, compilatorul trebuie s genereze

    adresele de memorie asociate componentelor respective. Modul de determinarea a adresei

    componentelor tablourilor va fi descris ntr-un paragraf ulterior.

    Exemplul 8.1. Se consider o mulime finit de elemente X = {x1, x2, , xn}. O relaie de

    compunere algebric pe mulimea X este o funcie p de forma:

    p: XX X

    S se determine dac o relaie algebric dat este comutativ sau nu.

    Descrierea algoritmului. Pentru reprezentarea relaiei de compunere algebric se pot utiliza dou

    tablouri: un vector X=(xi), i=1,,n pentru memorarea elementelor mulimii X i o matrice

    P=(pij), i=1,,n, j=1,,n pentru reprezentarea elementelor funciei p. Deoarece problema cere

    s se determine doar dac relaia este comutativ, mrimile de intrare ale problemei vor fi doar

    numrul n de elemente ale mulimii X, precum i matricea P. n aceste condiii, relaia de

    compunere p este comutativ dac matricea asociat P este simetric. Se consider c mulimea

    X are ca elemente numere reale.

    Descrierea programului.

    #include

  • int main(void) {

    int i, j, n, comutativ=1;

    float p[20][20];

    printf(\nIntroduceti n: );

    scanf(%d, &n);

    printf(\nIntroduceti elementele relatiei: );

    for(i=0; i

  • elementelor componente, separate prin virgule i nchise ntre acolade. Expresiile folosite pentru

    iniializare trebuie s fie expresii constante, care se pot evalua la compilare.

    Exemple. int v[6] = {1, 2, 1, 2, 1, 2} ;

    double a[2][3] = { {0, 0, 0}, {1, 1, 1} } ;

    double b[2][3] = { 0, 0, 0, 1, 1, 1 } ;

    float x[] = {1, 1, 1, 1} ;

    char t1[] = "Exemplu" ;

    char t2[8] = {E, x, e, m, p, l, u, \0} ;

    n exemplul anterior, tabloul x are patru elemente, dimensiune determinat din numrul de

    elemente iniializate.

    Observaii.

    1. n cazul tablourilor multidimensionale, fiecare element compus se specific ntre acolade. Acest lucru nu este obligatoriu, dac elementele componente se specific n ordinea de

    stocare n memorie. n exemplul precedent, tablourile a i b au aceeai valoare iniial.

    2. n cazul tablourilor de caractere, iniializarea se poate efectua fie prin specificarea fiecrui caracter ca element de tablou (cazul tabloului t2), fie prin specificarea irului de caractere

    drept o constant de tip ir de caractere (cazul tabloului t1) . n al doilea caz se adaug la

    sfrit caracterul cu codul zero. n exemplul precedent, tablourile t1 i t2 au aceeai

    iniializare.

    8.2 Pointeri i adrese de memorie

    Tipurile pointer reprezint, alturi de tipurile tablou, cele mai utilizate tipuri derivate ale

    limbajului C. Mai mult, pointerii confer limbajului o deosebit for i flexibilate.

    Obiectele majoritii tipurilor de date sunt elemente statice, n sensul urmtor: n

    momentul declarrii unei variabile, acesteia i se aloc o zon liber de memorie unde variabila i

    pstreaz valoarea n timpul execuiei programului, iar perechea ( , ) rmne aceeai n tot timpul execuiei programului. Pointerii permit utilizarea

    obiectelor dinamice, permind utilizatorilor crearea i distrugerea n mod explicit a acestora.

    Crearea unui obiect nseamn de fapt alocarea unei zone de memorie pentru obiectul respectiv,

    iar distrugerea obiectului nseamn dealocarea zonei de memorie aferente.

    Un pointer este o variabil care conine adresa de memorie a unui obiect (indicnd astfel

    spre obiectul respectiv), permind accesarea indirect a obiectului. ntruct exist moduri

    diferite de reprezentare intern pentru diferite tipuri de date, declararea unei variabile pointer

    trebuie s indice tipul de date al obiectelor spre care aceasta indic.

    Exemplu. Se definete variabila pointer pn care va indica spre obiecte de tip ntreg i variabila

    px care va indica spre obiecte de tip real. int *pn;

    float *px;

  • Definirea pointerilor utilizeaz operatorul de derivare *, care se deosebete (din context) de

    operatorul de nmulire. n definiia anterioar, int* reprezint tipul de date pointer spre tipul

    ntreg, adic mulimea elementelor care indic spre obiecte de tip ntreg, iar int *pn;

    specific faptul c pn indic spre un obiect de tip int. n general, dac tip reprezint un tip

    de date, atunci notaia tip* reprezint tipul de date pointer care indic spre elementele tipului

    tip (adui mulimea elementelor ce indic spre obiecte ale tipului tip).

    Observaie. n cadrul unei definiii aferent unui pointer, prezena spaiului este nesemnificativ.

    De exemplu, urmtoarele definiii sunt echivalente: int *pn;

    int * pn;

    int* pn;

    int*pn;

    Definiia pointerilor se poate face mpreun cu definirea altor elemente aparinnd tipului de date

    spre care pointerii indic. De exemplu, declaraia: int *p, k, m;

    definete variabila p ca fiind un pointer ca indic spre obiecte de tipul int, iar variabilele k i m

    ca fiind de tipul int.

    Pentru a atribui unui pointer adresa de memorie a unei variabile, se poate utiliza operatorul de

    adresare &, ca n exemplul urmtor: p = &k;

    ceea ce nsemn c variabila p indic spre obiectul k.

    Accesarea obiectului referit de un pointer se face prin intermediul operatorului de dereferire (se

    utilizeaz tot caracterul *), ca n urmtorul exemplu: m = *p;

    Operatorul de dereferire se deosebete din context de operatorii de nmulire i de derivare

    reprezentai de acelai caracter.

    Observaie. n cadrul unor expresii, p reprezint adresa de memorie a unui obiect, iar *p

    reprezint coninutul zonei de memorie respective (obiectul nsui).

    Crearea i distrugerea obiectelor dinamice se realizeaz prin intermediul unor funcii standard.

    Declaraiile referitoare la acestea se afl n fiierul standard alloc.h. Cele mai utilizate funcii

    sunt: malloc pentru alocarea unei zone de memorie i free pentru dealocarea unei zone de

    memorie alocate anterior. Memoria pentru obiectele dinamice se aloc ntotdeauna n zona heap

    a programului.

    Funcia free are un singur parametru reprezentat de o variabil pointer, efecul ei

    constnd n eliberarea zonei de memorie aferent pointerului specificat ca parametru. Prototipul

    funciei este: void free(void* );

    Funcia malloc are un parametru ntreg reprezentnd numarul de octei de memorie ce se

    doresc a fi alocai, efectul ei constnd n alocarea unei zone de memorie libere de o dimensiune

  • specificat i returnarea adresei de nceput a acesteia. n cazul acestei funcii apare o problem

    referitoare la tipul de date al obiectului care se va reprezenta n respectiva zon de memorie.

    ntruct funcia malloc va fi folosit pentru crearea obiectelor dinamice de diverse tipuri,

    rezultatul returnat de funcie este de tipul void*. n acest fel, programatorul trebuie s modifice

    tipul de date rezultat n funcie de tipul obiectelor dinamice create, folosind un operator de

    conversie de tip. Prototipul funciei este: void* malloc(size_t );

    Observaie. size_t reprezint o redefinire a tpului ntreg fr semn, fiind tipul rezultatului

    returnat de operatorul sizeof.

    Exemple. n urmtoarea secven de program se definete un pointer la un tip ntreg, se creaz

    un obiect dinamic (indicat de pointer), se iniializeaz cu o valoare i apoi se distruge obiectul

    creat. int *p;

    p = (int*)malloc(sizeof(int));

    *p = 3;

    free(p);

    Rezultatul returnat de funcia malloc este convertit la tipul int* pentru a fi compatibil cu

    poinerul p. Parametrul fuciei malloc trebuie s indice numrul de octei utilizai pentru

    reprezentatrea obiectelor de tip int. ntruct reprezentarea obiectelor tipului int este

    dependent de calculator, se folosete operatorul sizeof, care returneaz numrul de octei

    ocupat de tipul de date al parametrului.

    n concluzie, dac T reprezint un tip de date i pointerul p este definit ca: T *p;

    atunci alocarea memoriei pentru un obiect dinamic de tipul T se realizeaz n mod uzual astfel: p = (T*)malloc(sizeof(T));

    Observaie. Un apel de forma: free(p); nu se poate face dect dac anterior s-a alocat

    memorie pentru pointerul p, altfel execuia programului va fi eronat.

    n afar de malloc, se mai pot utiliza i alte funcii de alocare a memoriei. Iat principalele

    funcii:

    1) Funcia calloc, aloc n blocuri consecutive de memorie i returneaz adresa de nceput a

    zonei alocate, sau NULL n cazul n care nu se poate aloca memorie. n plus, ea iniializeaz

    zona de memorie alocat cu zero. void* calloc(size_t , size_t );

    2) Funcia ralloc ncearc s realoce memoria pentru un anumit bloc de memorie,

    actualiznd mrimea blocului, dac este posibil. n acest caz trebuie specificat adresa

    blocului alocat anterior i noua mrime a acestuia. n cazul n care nu exist memorie

    suficient pentru noua mrime a blocului, acesta rmne nemodificat, altfel, zona de

    memorie se extinde pn la mrimea specificat. Funcia returneaz adresa de memorie a

    blocului realocat, sau NULL dac nu s-a putut realoca memorie. La primul apel al acestei

  • funcii, adresa blocului de memorie alocat anterior este NULL i funcia realloc se

    comport identic funciei malloc. void* realloc(void* , size_t );

    Exemplul 8.2. Urmtorul program are doar un scop didactic i folosete obiecte dinamice pentru

    determinarea mediei aritmetice a trei numere reale.

    #include

    #include

    int main(void) {

    float *pmed, *px1, *px2, *px3;

    px1 = (float *)malloc(sizeof(float);

    px2 = (float *)malloc(sizeof(float);

    px3 = (float *)malloc(sizeof(float);

    pmed = (float *)malloc(sizeof(float);

    printf(\nIntroduceti x1, x2, x3: );

    scanf(%f%f%f, px1, px2, px3);

    *pmed = (*px1 + *px2 + *px3)/3;

    printf(\nmed=%f, *pmed);

    return 0;

    }

    Observaii.

    1. ntruct variabilele pointer (px1, px2 i px3 n exemplul anterior) reprezint chiar adrese se

    memorie, ele nu mai sunt prefixate de operatorul de adresare & in cadrul funciei scanf.

    2. Distrugerea obiectelor dianmice se face cu ajutorul funciei free, iar dac aceasta nu apare,

    distrugerea se realizeaz automat la sfritul execuiei programului, cum este cazul

    exemplului precedent.

    8.3 Pointeri i tablouri. Aritmetica pointerilor

    Spre deosebire de alte limbaje de programare, limbajul C permite efectuarea anumitor operaii

    aritmetice asupra pointerilor, operaii permise n general n cadrul limbajelor de asamblare.

    Aceste operaii evideniaz strsa legtur care exist ntre pointeri i tablouri.

    Principalii operatori aritmetici ce pot fi utilizai n cazul pointerilor reprezint operaiile

    aditive: +, -, ++, --. Toi aceti operatori necesit cel puin un operand de tip pointer.

    Operaiile de adunare i scdere se pot defini ntre pointeri sau ntre pointeri i numere

    ntregi:

    - n cazul adunrii operandul din stnga este obligatoriu un pointer, operandul din dreapta este un ntreg, iar rezultatul reprezint tot un pointer de aclai tip cu

    operandul stng. Dac, de exemplu, T reprezint un tip de date i n este o expresie

    ntrag, iar un poiner p este definit ca: T *p;

    Atunci rezultatul evalurii expresiei: p+n

  • este un pointer de tipul T a crui valoare este egal cu valoarea lui p, la care se adaug

    valoarea: n*sizeof(T)

    Cu alte cuvinte, p+n reprezint adresa celui de-al n-lea obiect consecutiv dup cel

    indicat de p.

    Observaie. n cazul adunrii, nu se pot aduna doi pointeri, chiar dac sunt de acelai

    tip.

    - n cazul scderii operandul din stnga este obligatoriu un pointer, iar operandul din dreapta poate fi un pointer de aselai tip, sau un ntreg:

    o n cazul n care operandul al doilea este un ntreg, rezultatul reprezint tot un pointer de aclai tip cu operandul stng. Dac T reprezint un tip de date i n

    este o expresie ntrag, iar un poiner p este definit ca: T *p;

    atunci rezultatul evalurii expresiei: p-n

    este un pointer de tipul T a crui valoare este egal cu valoarea lui p, la care se

    scade valoarea: n*sizeof(T)

    Cu alte cuvinte, p-n

    reprezint adresa celui de-al n-lea obiect naintea celui indicat de p.

    o n cazul n care operandul al doilea este un pointer, de acelai tip cu tipul primului operant, rezultatul este un ntreg ce reprezint numrul de obiecte de

    acelai tip dintre adresele indicate de cei doi pointeri. S considerm

    urmtoarea secven de cod: int v[10] = {0};

    int i = 4, j = 2, n;

    int* p = &v[i];

    int* q = &v[j];

    n = p q;

    atunci: Eval(p-q) = Eval(i-j) = 2

    Legtura ntre tablouri i pointeri se poate observa simplu din urmtorul exemplu. Dac se

    consider declaraiile: int v[] = {1, 2, 3};

    int* p = &v[0];

    int n = 1;

    atunci expresia v[n] este echivalent cu *(p+n), iar expresia &v[n] este echivalent cu

    p+n. Cu alte cuvinte, tablourile sunt considerate de ctre compilator ca nite pointeri constani:

    variabila v din definiia anterioar este considerat ca un pointer de tipul const int*, a crei

    valoare este iniializat la adresa de memorie a primului element al tabloului, iar aceast valoare

    nu poate fi modificat n timpul execuiei programului.

    n mod asemntor se poate defini operaia de scdere: p-n poate fi interpretat ca fiind

    expresia p+(-n) i reprezint adresa celui de-al n-lea element anterior celui indicat de p.

  • Observaie. n cazul tablourilor se pot ns referi elemente aflate n afara zonai de memorie

    alocate, ceea ce impune o atenie n ceea ce privete valoarea indicelui unui element al tabloului

    (compilatorul nu face o verificare a depirii zonei de memorie ataat tabloului). De exemplu,

    considernd un tablou a cu 15 componente de tip int i n o variabil de tip int, atunci expresia

    &a[-n] este echivalent cu a-n, adic este egal cu valoarea: a n * sizeof(int)

    ceea ce reprezint o adres de memorie corect pentru compilator. n mod asemntor, expresia

    &a[n] este corect chiar dac n depete numrul maxim de componente al tabloului (15),

    deoarece valoarea: a + n * sizeof(int)

    reprezint o adres de memorie corect.

    Se poate acum descrie modul de determinare a adresei de memorie pentru un element dintr-un

    tablou. n cazul tablourilor unidimensionale, referirea unei componente a[i] se face simplu,

    compilatorul genernd expresia echivalent: *(&a[0] + i)

    ceea ce din punctul de vedere al pointerilor nseamn *(a+i).

    n cazul tablourilor multidimensionale, va trebui determinat numrul de elemente consecutive

    aflate ntre primul element al tabloului i elementul curent ce se dorete s fie referit.

    S considerm cazul tablourilor bidimensionale, n care un tablou a are n linii i m coloane: const int n = 2;

    const int m = 3;

    S presupunem c se dorete referirea elementului a[i][j]. Notnd cu k numrul de elemente

    consecutive din tablou aflate ntre primul element a[0][0] i elementul curent, expresia

    echivalent generat de compilator pentru a[i][j]este: *(&a[0][0] + k)

    Relaia ntre i, j i k se poate determina simplu, dac se numr toate elementele aflate pe liniile

    0, 1, , i-1, la care se adaug cele j elemente de pe linia i. Deoarece toate liniile au m elemente,

    se poate scrie: k = m*i+j

    Rezult c formula generat de compilator la evaluarea expresiei a[i][j]este: (&a[0][0] + m*i + j)

    Observaie. n relaia anterioar nu apare numrul maxim de linii al tabloului a. Din acest motiv,

    exist situaii n care compilatorul permite specificarea incomplet a tablourilor.

    Formula de acces pentru componentele unui tablou tridimensional, sau n cazul general k-

    dimensional se poate deduce n mod asemntor.

    Expresiile ce conin pointeri i operatori de incrementare/decrementare se interpreteaz n

    acelai mod: ei permit indicarea spre elementul succesor/predecsor al elementului curent indicat

    de pointerul p. n acest caz ns pot apare confuzii legate de ordinea de evaluare a celor doi

    operatori (operatorul de dereferire i cel de incrementare/decrementare). Trebuie reinut faptul c

    operatorii de incrementare/decrementare sunt mai prioritari dect operatorul de dereferire.

  • Urmtorul tabel conine principalele expresii cu aceti operatori combinai (s-a specificat n

    tablou doar operatorul de incrementare):

    *p++ Se aplic nti operatorul de dereferire (se evalueaz obiectul referit de

    p, care este i valoarea de evaluare a expresiei) i apoi se aplic

    operatorul de incrementare (se incrementeaz pointerul) *++p Se incrementeaz nti valoarea pointerului i apoi se aplic operatorul

    de dereferire (se evalueaz obiectul referit de acesta) (*p)++ Se aplic nti operatorul de dereferire (se evalueaz obiectul referit de

    p) i apoi cel de incrementare (se incrementeaz valoarea acestui obiect) ++*p Se aplic nt operatorul de incrementare (se incrementeaz obiectul

    referit de p) i apoi cel de dereferire (valoarea rezultat n urma

    incrementrii fiind i valoarea de evaluare a expresiei)

    Din tabelul precedent se observ faptul c primele dou expresii acioneaz asupra pointerului,

    pe cnd ultimele doua supra obiectului indicat de acesta.

    n mod asemntor se pot descrie expresiile combinate ce conin operatori de

    decrementare i de dereferire.

    n cazul n care ambii operanzi dint-o expresie sunt pointeri, singura operaie permis este

    cea de scdere. n acest caz, ambii pointerii trebuie sa aib acelai tip de baz. Dac p1 i p2 sunt

    pointeri la un tip T, atunci valoarea expresiei p1-p2 este un ntreg ce cpecific numrul de

    obiecte de tipul T aflate ntre obiectele indicate de p1 i p2.

    Pe baza operaiei de scdere a pointerilor se definesc operatorii de relaie aplicai pointerilor,

    conform tabelului urmtor (p1 i p2 sunt pointeri asociai aceluia tip de date de baz):

    Expresie Semnificaie p1 < p2 p1 p2 < 0

    p1 0

    p1 >= p2 p1 p2 >= 0

    n cazul tablourilor multidimensionale se pot scrie expresii mai mult sau mai puin complexe n

    care apar operatori de indexare i de dereferire. Semnificaia evalurii unei asemenea expresii se

    poate determina relativ simplu dac se ine seama de precedena operatorilor. Tabelul urmtor

    prezint cteva exemple de utilizare a acestor operatori n cazul unui tablou bidimensional,

    considernd declaraia urmtoare: int a[3][4];

    Expresie Semnificaie a Pointer la un vector de 4 elemente int (prima linie a matricii)

    *a Pointer la un element int (prima linie, prima coloan)

    **a Un element int (prima linie, prima coloan)

    a[1] Pointer la un element element int (linia a doua, prima coloan)

    *a[1] Un element element int (linia a doua, prima coloan)

  • a+1 Pointer la un vector de 4 elemente int (linia a doua)

    *a+1 Pointer la un element int (prima linie, a doua coloan)

    a[1][1] Un element int (linia a doua, coloana a doua)

    a[1]+1 Pointer la un element element int (linia a doua, coloana a doua)

    n general, pentru a nelege o referin la un tablou multidimensional trebuie avute n vedere

    urmtoarele observaii:

    specificarea complet a unei referine la un tablou reprezint un element al tabloului;

    specificarea incomplet a unei referine se substituie cu un pointer;

    un nivel de indirectare este echivalent cu un pointer.

    8.4 Alocarea dinamic a tablourilor

    Unul dintre principalele dezavantaje ale utilizrii tablourilor este acela c ofer o alocare static a

    elementelor componente, n sensul c pentru fiecare tablou definit ntr-un program se aloc un

    numr fix de componente stabilit n etapa de compilare, indiferent de numrul real de

    componente folosit n timpul execuiei programului. Mecanismul pointerilor permite utilizarea

    tablourilor dinamice, pentru care alocarea memoriei se face n timpul execuiei programelor, dar

    n acest caz alocarea trebuie realizat n mod explicit de ctre programator.

    n cazul alocrii dinamice a unui tablou unidimensional, se poate folosi o variabil de tip

    pointer care indic spre primul element din tablou. De exemplu, pentru un tabou de numere

    ntregi, pointerul se declar astfel: int *pv;

    Alocarea memoriei pentru n elemente de tip int se poate realiza cu ajutorul funciei malloc, ca

    n figura 8.1. pv = (int*)malloc(n*sizeof(int));

    Referirea unui element dintr-un asemenea tablou alocat dinamic se poate face utiliznd

    operatorul de indexare: pv[i]

    ceea ce este tradus de ctre compilator ca: *(pv+i)

    Dezalocarea memoriei pentru un tablou alocat dinamic se poate realiza simplu cu ajutorul

    funciei free: free(pv);

    0 1 n-1

    pv

    Figura 8.1. Alocarea memoriei pentru o valore de tip int

  • n cazul tablourilor bidimensionale, o metod simpl este aceea n care o asemenea matrice este

    privit ca un vector de pointeri, fiecare element din vector indicnd la o linie a matricii. Pentru

    aceasta, se va utiliza un tablou unidimensional asociat liniilor, fiecare element al tabloului

    memornd adresa primului element din linia respectiv. De exemplu, pentru un tablou

    bidimensional ale crui componente sunt de tip int, declararea se poate face astfel: int **pa;

    iar sugestiv, alocarea memoriei arat ca n figura 8.2.

    Alocarea memoriei se face separat pentru vectorul de pointeri la linii, ct i pentru fiecare linie a

    tabloului. De exemplu, dac se dorete alocarea unui tablou cu n linii i m coloane, o funcie de

    alocare dinamic se poate scrie astfel:

    int** AlocaMatrice(int n, int m) {

    int k;

    int **pa = (int**)malloc(n*sizeof(int*));

    for(k=0; k

  • Pentru eliberarea memoriei, trebuie dezalocat att zona de memorie asociat elementelor

    liniilor, ct i cea asociat pointerilor la linii:

    void DealocaMatrice(int **pa, int n) {

    int k;

    for(k=0; k

  • for(j=0; j
  • char *ps = Alt sir;

    este tratat de compilator astfel: se rezerv n memorie 8 octei consecutivi n care se scriu

    caracterele: A, l, t, , s, i, r, \0, iar adresa primului octet

    se atribuie pointerului ps ca valoare de iniializare. n concluzie, pentru un ir de n caractere,

    compilatorul reuerv n+1 locaii de memorie, n locaia n+1 fiind scris caracterul \0.

    Observaii.

    1. Constantele ir de caractere se pot utiliza n locul pointerilor la caractere, pentru c la evaluarea unei astfel de constante se returneaz adresa de memorie a primului caracter din ir

    (care are tipul char*). De exemplu, expresia abcdefgh+2 are ca valoare adresa de

    memorie a caracterului c, iar valoarea expresiei *(abcdefgh+3) este caracterul

    d.

    2. Ca o consecin a observaiei anterioare, rezult c o constant ir de caractere poate fi specificat n locul unui nume de tablou de caractere cnd se folosete operatorul de adresare.

    De exemplu, expresia abcdefgh[3] este corect i are ca valoare de evaluare tot

    caracterul d.

    Pentru citirea i scrierea irurilor de caractere se poate utiliza specificatorul de format %s. n

    acest caz este indicat utilizarea unei variabile de tip tablou de caractere i nu un pointer la

    caractere, deoarece n ultimul caz compilatorul nu rezerv memorie dect pentru pointer i este

    posibil s nu se mai detecteze caracterul de sfrit al irului. n exemplul urmtor, al doilea apel

    al funciei scanf poate conduce la rezultate eronate: char s[10], *ps;

    scanf(%s, s);

    scanf(%s, ps);

    Pentru a suplini faptul c limbajul C nu posed un tip de date predefinit pentru irurile de

    caractere i nu ofer operatori specifici, sunt puse la dispozie numeroase funcii de bibliotec

    pentru prelucrarea acestora. n continuare sunt prezentate cteva funcii mai des utilizate, a cror

    declarare de afl n fiierele header string.h i stdio.h.

    A) Funcia gets citete un ir de caractere de la terminalul standard de intrare: char* gets(char* );

    irul citit se termin la prima apariie a caracterului \n i va fi scris la adresa indicat de

    , dar fr caracterul \n (dar cu caracterul \0 scris la sfritul irului).

    B) Funcia puts scrie un ir la terminalul standard de ieire: int puts(const char* );

    Ea returneaz ultimul caracter scris sau EOF n caz de eroare. Aceste dou funcii sunt declarate

    n fiierul stdio.h.

    C) Funcia strcpy este utilizat pentru copierea unui ir de caractere: char* strcpy(char* , const char* );

    Ea copiaz irul de caractere (inclusiv terminatorul de ir) din zona de memorie indicat de

    pointerul n zona de memorie indicat de pointerul .

  • Pentru o mai bun nelegere a modului de operare cu iruri de caractere, n continuare se

    prezint cteva variante ale unei funcii numite MyStrcpy, care realizeaz aceeai operaie ca i

    strcpy.

    Exemplul 8.4.

    char* MyStrcpy1(char* destin, const char* sursa) {

    char *temp = destin;

    while((*destin = *sursa) != \0) {

    destin++;

    sursa++;

    }

    return temp;

    }

    char* MyStrcpy2(char* destin, const char* sursa) {

    char *temp = destin;

    while((*destin++ = *sursa++) != \0);

    return temp;

    }

    char* MyStrcpy3(char* destin, const char* sursa) {

    char *temp = destin;

    while(*destin++ = *sursa++);

    return temp;

    }

    D) Funcia strcat concateneaz dou iruri de caractere: char* strcpy(char* , char* );

    Funcia adaug o copie a irulului la sfritul irului i returneaz irul

    rezultat.

    E) Funcia strlen determin lungimea unui ir, returnnd numrul de caractere din ir,

    exclusiv terminatorul: size_t strlen(const char* );

    F) Funcia strcmp compar lexicografic dou iruri dou iruri: int strcpy(const unsigned char* ,

    const unsigned char char* );

    Valoarea returnat este zero dac cele dou iruri sunt egale, un numr negativ dac primul ir

    este mai mic (n odrine lexicografic) dect cel de-al doilea i un numr pozitiv dac primul ir

    este mai mare dect al doilea.

    G) Funcia strchr caut prima apariie a unui caracter ntr-un ir: char* strchr(const char* , int );

    Funcia returneaz adresa primei aparitii a caracterului n ir sau NULL dac irul nu conine

    caracterul cutat.

  • H) Funcia strrchr realizeaz tot o cutare a unui caracter ntr-un ir, dar n sens invers: char* strrchr(const char* , int );

    Funcia returneaz adresa ultimei aparitii a caracterului n ir sau NULL dac irul nu conine

    caracterul cutat.

    I) Funcia strstr caut un subir de caractere ntr-un ir: char* strstr(const char* , const char* );

    Funcia caut irul de caractere n irul specificat i returneaz un pointer la primul caracter din

    prima apariie a subirului n ir; dac subirul nu a fost gsit se returneaz NULL.

    Un alg grup de funcii se refer la conversia irurilor de caractere n valori numerice. De

    exemplu, irul s = 123 poate fi convertit n numrul ntreg 123 sau n numrul real

    corespunztor. n mod uzual declaraiile acestor funcii se afl n fiierul stdlib.h.

    funcia atoi convertete un ir la o valoare ntreg: int atoi(const char* );

    funcia atol convertete un ir la o valoare ntreg de tip long: long atol(const char* );

    funcia atof convertete un ir la o valoare real de tip double: double atof(const char* );

    Pentru exemplificare se va rezolva o problem de prelucrare a irurilor de caractere.

    Exemplul 8.5. De la terminalul standard de intrare se introduce un text de maxim 256 caractere.

    Pentru simplitate, se presupune ca textul se termin la apsarea tastei ENTER i este format din

    propozii terminate cu caracterul punct, iar propoziiile conin cuvinte separate de un spaiu. S

    se afieze pe ecran textul pe linii, considernd c fiecare linie are cel mult N=82 caractere.

    Cuvintele nu se vor despri pe dou linii, cu execpia celor ce au mai mult de N caractere.

    Descrierea algoritmului. Se vor utiliza dou tablouri de caractere, unul pentru citirea textului,

    notat text, iar al doilea pentru afiarea unei linii curente, notat linie. Din irul text se vor extrage

    pe rnd subirurile ce formeaz o linie curent de afiare.

    Pentru determinarea subirului ce formeaz o line de afiare se vor utiliza trei pointeri, p,

    p1 i p2 ca n figura 8.3, cu urmtoarea semnificaie: p indic spre caracterul din text de la care

    ncepe linia curent de afiare, p1 indic spre un caracter spaiu ntlnit n text, iar p2 spre primul

    spaiu dup p1. Un cuvnt din text curent din text este ncadrat ntre p1 i p2.

    Cu ajutorul pointerilor p1 i p2 se va determina primul cuvnt din text care nu mai ncape pe

    linia curent, adic acel cuvnt pentru care poz(p1)N i poz(p2)N. Am notat cu

    poz(pi) este poziia relativ a caracterului indicat de pi la caracterul indicat de p (adic pi-p)

    Structura algoritmului este urmtoarea:

    p p1 p2

    text

    Figura 8.3. Utilizarea pointerilor pentru specificarea unui subir

  • start

    citeste text

    p &text[0]

    p2 p

    cat timp pNULL executa

    p1 p2

    p2 strstr(p, )

    daca (p2NULL) (p2-p>N) atunci

    daca p2-p1>N atunci

    *scrie din text in linie N caractere

    incepand de la de la pointerul p *

    p p+N+1

    altfel

    *scrie din text in linie caracterele

    de la pointerul p la pointerul p1*

    p p1+1

    p2 p

    scrie linie

    altfel

    daca p2=NULL atunci

    *scrie din text in linie caracterele

    ramase*

    scrie linie

    p NULL

    sfarsit

    Descrierea programului.

    #include

    #include

    #define L_TEXT 256

    #define L_LINIE 82

    int main() {

    char text[L_TEXT], linie[L_LINIE], spatiu[] = ;

    char *p, *p1, *p2, *r;

    int N, k;

    printf(\nNumarul maxim de caractere pe linie: );

    scanf(%d, &N);

    getchar(); /* sare peste caracterul ENTER */

    printf(\nIntroduceti textul:\n);

    gets(text);

    printf("\nTextul formatat este:\n");

  • p = text;

    p2 = p;

    while (p) {

    p1 = p2;

    p2 = strstr(p1, spatiu)+1;

    if (p2 && p2-p>N) {

    if (p2-p1>N) {

    r = linie;

    k = 0;

    while (k++

  • 8.1. S se scrie formula de acces pentru componentele unui tablou tridimensional i n cazul general pentru un tablou k-dimensional, n mod asemntor celei prezentate n paragraful

    8.3.

    8.2. Se consider un numr ntreg n. S se determine dac n este un numr palindrom (cifrele egal deprtate de extrem sunt identice).

    8.3. Se consider o matrice ptrat de numere reale, A=(aij), i,j=1,...,n. S se determine: a) Dac matricea este unitate; b) Dac matricea este superior triunghiular.

    8.4. Se consider un polinom cu coeficieni reali, specificat prin gradul su i vectorul coeficienilor:

    P=a0+a1X+a2X2+ ... +anX

    n

    S se determine:

    a) Valoarea polinomului ntr-un punct dat x0; b) Polinomul obinut prin derivarea polinomului iniial

    8.5. Se consider un polinom de dou variabile, P(X,Y), specificat prin numrul termeni din polinom, vectorul coeficienilor (cte un coeficient pentru fiecare termen) i o matrice cu

    un numr de linii egal cu numrul de termeni i dou coloane reprezentnd puterile celor

    dou variabile n termenul corespunztor. S se determine:

    a) Valoarea polinomului ntr-un punct dat (x0, y0); b) Polinomul rezultat prin derivarea polinomului iniial n raport cu variabila X.

    8.6. Se consider un ir de numere reale x1, x2, ..., xn. S se determine: a) Dac un alt numr real dat, x0, face parte din termenii irului; b) Toate subirurile irului iniial care sunt ordonate cresctor; c) Cel mai lung subir al irului iniial ordonat cresctor.

    8.7. Se consider un ir de numere reale x1, x2, ..., xn ordonat cresctor. Considerndu-se n plus un numr real x0, s se insereze acest numr printre elementele irului iniial, aa nct

    el s rmn ordonat i dup inserare.

    8.8. S se elaboreze un algoritm i un program care s sorteze cresctor un ir de numere prin metoda inserrii: se vor insera pe rnd elementele n ir, astfel nct irul parial obinut

    dup inserarea elementului curent s fie de asemenea ordonat cresctor.

    8.9. S se modifice programul de la problema 8.8, astfel nct s permit citirea succesiv a mai multor expresii, pn cnd primul caracter citit de pe rndul curent este caracterul

    punct. Dup fiecare citre se va afia rezultatul, nainte de citirea rndului urmtor.

    8.10. Se consider n puncte n plan de coordonate: (x1, y1), (x2, y2), ..., (xn, yn). a) S se determine dac poligonul cu vrfurile n punctele date este convex sau nu.

  • b) S se determine: centrul de greutate al poligonului cu vrfurile n punctele date, perimetrul i aria poligonului.

    c) Considerdu-se un alt unct de coordonate (x0, y0), s se determine poziia sa fa de poligon(n interior, n exterior sau pe perimetru).

    8.11. Se consider dou poligoane specificate prin coordonatele vrfurilor n ordine trigonometric. S se determine dac intersecia acestora este vid sau nu.

    8.12. Se consider n plan o mulime de puncte specificat prin coordonatele lor: M = { (x1, y1), (x2, y2), ..., (xn, yn) }. S se determine nfurtoarea convex a mulimii de puncte.

    Indicaie. nfurtoarea convex a unei mulimi M de puncte coplanare este poligonul

    convex ce conine ca vrfuri o submulime P puncte din M, P M, iar n interior celelalte

    puncte din mulimea M - P.


Recommended