Kihagyás

9. gyakorlat

(A hosszú hétvége miatt számozásban kimarad a 8-as.)

Téma: Erőforrások kezelése (part 1)

  • Az erőforrásokról
  • Dinamikus memóriakezelés

Erőforrások

Minden erőforrást hasonlóan kell kezelni

Általános szabályok:

  1. Hozzáférést kérni az erőforráshoz
  2. Ellenőrizni, hogy megkaptuk-e a hozzáférést
    • Ha sikertelen, akkor jogosulatlan erőforrást használnánk
  3. Felszabadítani az erőforrást
    • Ha elmarad, akkor feleslegesen foglaljuk az erőforrást

Dinamikus memóriakezelés

Szükséges: standard library

#include <stdlib.h>
  • Az így foglalt terület bármeddig (amíg fel nem szabadítjuk) a rendelkezésünkre áll
  • Az ilyen memória több függvényen keresztül is használható
  • Általános típusú adat nincs => konvertálni kell a megfelelő típusra

I.) Hozzáférés kérése

malloc() allokáló függvény

void * malloc(size_t size)
  • malloc = memory allocation
  • Megadott byte-nyi méretű memóriát próbál lefoglalni
  • size_t: valamilyen unsigned egész (architektúra szerint)
  • Ha sikeres a foglalás: Visszatér egy a memória első címére mutató általános void pointerrel
  • Ha sikertelen: null pointer

Általános szintax:

malloc(mennyit)

calloc() allokáló függvény

void * calloc(size_t num, size_t size)
  • Ritkábban használt
  • Num darab size méretű objektumok foglalására
    • (Nem eltétlenül a két szám szorzata)
  • Ha sikeres a foglalás:
    • Visszatér egy a memória első címére mutató általános void pointerrel
    • Minden lefoglalt byte-ot 0-ra inicializál
  • Ha sikertelen: null pointer

Általános szintax:

calloc(mennyit, meret)

realloc() allokáló függvény

void* realloc(void * original, size_t new_size)
  • (Ezzel tudunk gyakorlatilag további területet foglalni egy korábban foglalt területhez)
  • A pointer által hivatkozott, már lefoglalt és fel nem szabadított memóriaterületet allokálja át egy másik memóriaterületre a megadott mérettel
  • Sikertelen foglalás esetén:
    • Null pointerrel tér vissza
    • A régi memóriaterület megmarad
  • Sikeres foglalás esetén:
    • Visszatér egy a memória első címére mutató általános void mutatóval
    • A régi memóriaterület felszabadul
    • A régi memóriaterületen lévő adat átmásolódik az új területre
    • Ha megadott méret nagyobb, mint az eredeti:
      • Az eredeti méret feletti memóriában memóriaszemét lesz (nincs inicializálva)
    • Ha a megadott méret kisebb, mint az eredeti:
      • Csak az ekkora méretnek megfelelő adat másolódik át
      • A többi elveszik

Általános szintax:

realloc(mutato_neve, uj_mennyi)

II.) Ellenőrzés

  • Sikertelen memória foglalás esetén null pointert (0-t) kapunk vissza => ezt használjuk fel az ellenőrzésre
  • Mit csinálhatunk? pl.:
    • Hibaüzenetet írunk ki és termináljuk a programot
    • Megpróbálunk újra foglalni

Példa:

if(p == 0)
{
    printf("Sikertelen foglalás\n");
}

// vagy

if(!p)
{
    printf("Sikertelen foglalás\n");
}

// vagy

if(p == NULL)
{
    printf("Sikertelen foglalás\n");
}

III.) Felszabadítás

free()

void free(void * pointer)
  • A memória felszabadítása a programozó dolga
  • Paraméter: a felszabadítandó területre mutató pointer
  • A felszabadítás mindig sikeres
  • A felszabadított memóriát már nem tudjuk használni <= segfault-ot kapnánk
    • Tipp: A felszabadítás után állítsuk 0-ra a pointert

Általános szintax:

free(mutato_neve)

Lehetséges hibák és okaik

  • Memory leak (memóriaszivárgás):
    • Kimarad a free() utasítás
    • A memóriára mutató pointert másik memóriacímre iránytjuk át
  • Double free
    • Futási hibát kapunk, ha egynél többször próbáljuk felszabadítani a foglal memóriát

Példák

#include <stdio.h>
#include <stdlib.h>

int main()
{
    // malloc
    // 4 db int-nek foglalunk memóriát
    // (int*) kasztolás a típusra
    int n = 4;
    int *p = (int*) malloc(n * sizeof(int));

    // ellenőrzés
    if (p == 0)
    {
        // hibaüzenet és a program terminálása
        printf("Sikertelen foglalás\n");
        return 0;
    }

    // kiíratás
    printf("A lefoglalt memóriacímek:\n");
    for (int i = 0; i < n; i++)
    {
        printf("%p\n", &p[i]);
    }

    // realloc
    n = 6;
    p = realloc(p, n * sizeof(int));

    // ellenőrzés
    if (p == 0)
    {
        // hibaüzenet és a program terminálása
        printf("Sikertelen foglalás\n");
        return 0;
    }

    // kiíratás
    printf("Az új memóriacímek:\n");
    for (int i = 0; i < n; i++)
    {
        printf("%p\n", &p[i]);
    }

    //felszabadítás
    free(fp);

    return 0;
}
#include <stdio.h>
#include <stdlib.h>

int main()
{
    // malloc
    // 4 db int-nek foglalunk memóriát
    // (int*) kasztolás a típusra
    int n = 4;
    int *p = (int*) malloc(n * sizeof(int));

    // ellenőrzés
    if (p == 0)
    {
        // hibaüzenet és a program terminálása
        printf("Sikertelen foglalás\n");
        return 0;
    }

    // bekérés és kiíratás
    printf("Kérem az adatokat:\n");
    for (int i = 0; i < n; i++)
    {
        scanf("%d", p+i);
    }

    printf("Az adatok:\n");
    for (int i = 0; i < n; i++)
    {
        printf("%d\n", *(p+i));
    }

    // realloc
    n = 6;
    p = realloc(p, n * sizeof(int));

    // ellenőrzés
    if (p == 0)
    {
        // hibaüzenet és a program terminálása
        printf("Sikertelen foglalás\n");
        return 0;
    }

    // kiíratás
    printf("Kérem az új adatokat:\n");
    for (int i = 0; i < 2; i++)
    {
        scanf("%d", p+4+i);
    }

    printf("Az adatok:\n");
    for (int i = 0; i < n; i++)
    {
        printf("%d\n", *(p+i));
    }

    //felszabadítás
    free(fp);

    return 0;
}