CURS 7 - Babeș-Bolyai Universityvchis/cursuri/info/c07ppt.pdfCURS 7 Pointeri - tipuri speciale de...

21
CURS 7 Pointeri - tipuri speciale de variabile sau constante care au ca valori adrese ale unor alte variabile, constante, functii, structuri de date (adrese ale unor locaţii de memorie) - permit calcule cu adrese - specifice limbajelor de asamblare - sunt folosiţi în scopul scrierii unor programe mai eficiente atât din punctul de vedere al timpului de execuţie cât şi din punctul de vedere al utilizării resurselor hard, în mod concret al utilizării memoriei computerelor. - sunt în mod special utili la alocarea dinamică a memoriei şi la apelul prin referinţă.

Transcript of CURS 7 - Babeș-Bolyai Universityvchis/cursuri/info/c07ppt.pdfCURS 7 Pointeri - tipuri speciale de...

  • CURS 7

    Pointeri - tipuri speciale de variabile sau constante care au ca valori adrese ale unor alte variabile,

    constante, functii, structuri de date (adrese ale unor locaţii de memorie)

    - permit calcule cu adrese

    - specifice limbajelor de asamblare

    - sunt folosiţi în scopul scrierii unor programe mai eficiente atât din punctul de vedere al timpului de execuţie cât şi din punctul de vedere al utilizării resurselor hard, în mod concret al utilizării memoriei computerelor.

    - sunt în mod special utili la alocarea dinamică a memoriei şi la apelul prin referinţă.

  • Pointerii - păstrează adresa începutului locaţiei în care este stocată o anumită valoare. - pachet compus din două părţi:

    - pointerul însuşi - care are ca valoare adresa primului octet al locaţiei de memorie în care este

    stocată o variabilă (sau constantă) - tipul valorii stocate în locaţia de memorie la începutul căreia pointează

    - spune computerului câtă memorie să citească după adresa la care pointează şi cum să o interpreteze

  • a) pentru a crea structuri dinamice de date construite din blocuri de memorie b) pentru a opera cu parametrii pasaţi funcţiilor la apelul acestora c) pentru a accesa informaţia stocată în tablouri

    Limbajul C foloseşte pointerii în trei moduri:

    În cazul unui pointer la o funcţie, tipul pointerului va fi tipul de dată returnat de către funcţie, iar valoarea sa va fi adresa la care începe codul funcţiei respective

    Pentru lucrul cu pointeri, limbajul C oferă doi operatori: &

    - &x înseamnă adresa variabilei x (de fapt adresa primului octet al locaţiei de memorie în care este stocată variabila x)

    * - *p1 valoarea stocată în locaţie de memorie la care pointează pointerul p1.

  • Declararea şi iniţializarea pointerilor

    tip *nume_pointer; sau tip* nume_pointer; tip - tip de date de bază sau tip utilizator nume_pointer - identificator folosit pentru pointerul care se declară

    Declarare

    float *pf; //pf poate conţine adrese la care se află memorate date de tip float

    int *pi; //pi poate conţine adrese la care se află memorate date de tip int

    char* pc; //pc poate conţine adrese la care se află memorate date de tip char

    struct agenda

    {

    char nume[20];

    char tel[15];

    };

    struct agenda *psa;

    //psa este un pointer la date de tip structură agenda.

    Exemple

  • double a, *pd;

    a=8.406;

    pd=&a;

    Exemple

    int n=1, m=2, s[10];

    int *pi; // pi este un pointer la tipul întreg

    pi = &n; // pi pointează acum la n

    m = *pi; // m are valoarea de la adresa lui n, adică 1

    *pi = 0; // n are acum valoarea 0

    pi = &s[0]; // pi pointează acum la s[0], adică are ca

    // valoare adresa elementului s[0]

    *pi=5*m; // s[0] are acum valoarea 5*1=5

    float pi, *p;

    p=π

    *p=3.14159; // se atribuie lui pi valoarea 3.14159

    int* p1, p2, p3; //numai p1 este pointer

    float *pf1, fact(int *); // se declară pointerul pf1 la tipul float şi o funcţie fact care returnează o valoare de tip float şi care are ca argument un pointer la tipul int.

    Un pointer este constrâns să pointeze la un anumit tip de date!!!

    Iniţializare

    - precizarea adresei unui obiect definit în memorie. - p - numele unui pointer => *p reprezintă valoarea de la adresa la care "pointează"

    pointerul p.

  • Program exemplu

    #include

    #include

    int main()

    {

    float a,b;

    float* p;

    p = &a;

    *p=4*atan(1.0);

    b=2*a;

    printf("a=%f b=%f",a,b);

    printf("\nValoarea din locatia de memorie la care pointeaza p este %f

    \nAdresa lui a: %X", *p, p);

    return 0;

    }

    Atenţie la iniţializarea pointerilor !!! + referinta nevalida + pointeri nuli

    Exemplu //testare pointer

    int* pi=0;

    ...

    if(pi!=0)

    n=*pi; //se foloseşte pointerul dacă pointează la o adresă legală (validă)

  • #include

    #include

    void main()

    {

    int* pi=NULL;

    int n;

    scanf("%d",&n);

    if(n

  • Operaţii cu pointeri

    a) Incrementare şi decrementare

    Fie declaraţia: int *p;

    Instrucţiunile: ++p; şi p++;

    respectiv: --p; şi p--;

    măresc, respectiv micşorează valoarea lui p cu o unitate (4 octeti)

    b) Adunarea şi scăderea unui întreg dintr-un pointer

    Rezultatul operaţiei p+n, unde p este un pointer şi n este un întreg este: p+n·r, unde r reprezintă numărul de octeţi folosiţi pentru păstrarea în memorie a datelor de tipul celor spre care pointează p.

    Exemplu Fie declaraţiile: int n,*pin=&n;

    float x,*pre=&x;

    Dacă pin are valoarea 1BBC, expresia pin+3 va avea valoarea 1BBC+6, adică 1BC2. Dacă pre are valoarea 1BB6, atunci expresia pre-5 va avea valoarea 1BB6-20, adică 1BA2.

  • c) Scăderea pointerilor int num , *ptr1 ,*ptr2 ;

    num=ptr1-ptr2; // rezultatul este un intreg, egal cu numarul de obiecte (locatii de memorie) dintre valorile pointerilor

    d) Compararea pointerilor

    Comparaţiile logice !=, ==, = sunt valabile şi în cazul pointerilor.

    Exemplu // functie pentru determinarea lungimii unui sir de caractere

    int lsir(char *pc)

    {

    int i;

    for(i=0;*pc!=NULL;pc++)

    i++;

    return i;

    }

    int main() { char sir[50]; gets(sir); printf(“Lungimea sirului este %d”,lsir(sir)); return 0; }

    Adunarea pointerilor nu este permisa!!! pmed=(pin+pfin)/2; // Invalid pointer addition pmed=pin+(pfin-pin)/2; este o instructiune corecta (la pin se adauga un intreg)

  • Pointeri şi tablouri Pointerii sunt intim legaţi de tablouri - numele unui tablou este un pointer constant care are ca valoare adresa elementului de index 0 al tabloului respectiv. - orice operaţie care se face folosind indicii tablourilor poate fi făcută, chiar mai rapid, prin folosirea pointerilor.

    Exemple 1. char text[10]="Anul1Fizica", *pc;

    pc=text;

    putchar(*pc); //se va tipări caracterul A

    pc+=4;

    putchar(*pc); //se va tipări caracterul 1

    2. char sir[10]=“DevC++";

    char *p;

    p=sir;

    while(*p)

    putchar(*p++); //se va tipări sirul “DevC++”

    3. double a[15],t;

    t=*(a+3); //este echivalent cu t=a[3];

  • Program exemplu: adunarea elementelor unui şir folosind pointeri #include

    #include

    #include

    #include

    int main()

    {

    float x[100], *y,v;

    double s=0.0,med;

    int i;

    //genereaza aleator elementele sirului x

    for(i=0;i

  • fără pointeri: #define DIM 50 int x[DIM],i; for(i=0;i

  • Apelul prin referinţă utilizând parametri de tip pointer

    Apelul funcţiilor prin valoare funcţiei i se transmite valoarea argumentului

    Apel: f(x) Antet: void f(int x) void printsir(float sir[], int dim)

    {

    int i;

    for (i=0; i

  • Program exemplu: ordonarea unui şir folosind pointeri şi funcţie care returnează un

    pointer la şirul ordonat

    #include

    void printsir(float *p, int dim)

    {

    int i;

    for (i=0; i

  • Program exemplu: funcţii swap cu apel prin valoare şi prin referinţă

    #include

    void sc1(double v1, double v2)

    {

    double t;

    t=v1;v1=v2;v2=t;

    }

    void sc2(double *i, double *j)

    {

    double t;

    t=*i;*i=*j;*j=t;

    }

    int main()

    {

    double a,b;

    printf("Adresa la care incepe codul functiei sc1 este: %X\n",sc1);

    scanf("%lf%lf",&a,&b);

    sc1(a,b);

    printf("\nApelul functiei sc1:\na= %lg\tb=%lg",a,b);

    sc2(&a,&b);

    printf("\nApelul functiei sc2:\na= %lg\tb=%lg",a,b);

    return 0;

    }

    sc1 nu interschimbă valorile variabilelor din funcţia apelantă ci numai copii ale acestora! sc2 schimba valorile variabilelor din functia apelanta.

  • Tema Să se scrie o funcţie care să returneze 1 dacă se citeşte un număr de la tastatură şi să

    returneze 0 în caz contrar. Vezi funcţia getint din B.W. Kernighan and D.M. Ritchie, The C

    Programming Language, pag. 81

    Pointeri la funcţii

    -numele unei funcţii este un pointer la funcţia respectivă -numele funcţiei este o reprezentare în mod intern a adresei la care începe codul funcţiei

    - se utilizează pentru transmiterea funcţiilor ca parametri ai altor funcţii.

    Exemplu Dacă dorim ca funcţia f să apeleze funcţia g sub forma f(g), funcţia g având antetul: float g(int x)

    atunci antetul lui f trebuie să fie de forma: double f (float (*) (int))

  • Exemplu

    //Funcţia bisect are ca parametru o altă funcţie f

    //======================= Functia f =========================

    double f(double x)

    {

    return x-sqrt(2);

    }

    //======================Functia bisect =======================

    double bisect(double inf, double sup, double (*pf)(double))

    {

    double c,sol;

    if((*pf)(inf)==0) return inf;

    if((*pf)(sup)==0) return sup;

    if((*pf)(inf)*(*pf)(sup)>0)

    {

    printf("\n\a\aNu exista sol. sau exista sol. multiple");

    getch();

    exit(1);

    }

    do

    {

    c=(inf+sup)/2.0;

    if((*pf)(c)==0) return c;

    if((*pf)(inf)*(*pf)(c)= eps);

    return c;

    }

    //=================Apelul functiei bisect=======================

    s=bisect(A,B,f);

  • Structuri de date dinamice în C

    - structuri care îşi modifică dimensiunea prin alocarea şi eliberarea (dealocarea) memoriei dintr-o zonă specială de memorie numită "heap“ - permit programatorului să controleze exact consumul de memorie al unui program. - managementul structurilor dinamice se face folosind pointeri

    => o utilizare eficientă a memoriei

    Memoria heap este o zonă aparte de memorie corespunzătoare unui program, folosită pentru crearea şi distrugerea structurilor de date care au timp de viaţă limitat.

    Memoria heap - zonă de memorie complet separată, controlată de către un manager de memorie de tip "run-time", care face managementul memoriei în timpul execuţiei programului. - este disponibilă programelor de aplicaţii în timpul execuţiei acestora folosind funcţiile malloc şi free.

    “read only”

  • Memoria stack (stiva) - regiune speciala de memorie care stocheaza variabilele temporare (locale) create de functii (inclusiv de catre functia main()) - variabilele din memoria “stack” dispar la iesirea din functie - este limitata (dimensiunea ei depinde de sistemul de operare)

    => erori de tipul “stack overflow” - managementul ei este facut de catre CPU (nu necesita managementul programatorului) - acces foarte rapid - “bug”: incercarea de a accesa o variabila locala a unei functii care a iesit din executie, de catre o alta functie.

    Memoria heap - variabilele pot fi accesate global, de catre orice functie - managementul ei trebuie facut de catre programator altfel => “memory leak” deoerece memoria alocata de o anumita aplicatie nu va deveni disponibila pentru alte aplicatii - se acceseaza folosind pointeri - nu este limitata d.p.d.v. al numarului de date stocate (evident, e limitata fizic) - accesul este mai putin rapid decat in cazul memoriei “stack” - recomandat sa fie folosita cand se doreste stocarea unui tablou sau a unei structuri de dimensiuni mari

  • Funcţia malloc - prototipul în şi în - alocă o zonă contiguă de memorie şi este de forma: (void *) malloc(dimensiune)

    dimensiune - mărimea blocului de memorie alocat în octeţi. - returnează un pointer la primul octet al primei locaţii a blocului de memorie alocat - tipul pointerului se va converti la tipul dorit al datelor care se vor stoca: int *pi,n; pi=(int *) malloc (n*sizeof(int));

    - in caz de eroare, sau daca dimensiune este egală cu zero, returnează pointerul NULL.

    Funcţia free - eliberează un bloc de memorie alocat cu funcţia malloc () - apelul acesteia se face cu un parametru care reprezintă pointerul la originea blocului de memorie care se eliberează.

  • Program exemplu: folosirea funcţiilor malloc şi free

    //Se aloca dinamic un tablou de n elemente;

    #include

    #include

    int main()

    {

    int n,i;

    float *p,*p0;

    printf("Introduceti dimensiunea tabloului: ");

    scanf("%d",&n);

    p=(float *)malloc(n*sizeof(float));

    //(float *) converteste pointerul void

    //returnat de catre malloc intr-un pointer la tipul float

    p0=p;

    if (p==NULL)

    {

    printf("Eroare: Memorie nedisponibila\n");

    exit(1);

    }

    for(i=0;i