C/malloc

< C
(Przekierowano z C/realloc)

Deklaracja

edytuj
#include <stdlib.h>

void *calloc(size_t nmeb, size_t size);
void *malloc(size_t size);
void free(void *ptr);
void *realloc(void *ptr, size_t size);

Argumenty

edytuj
nmeb
Liczba elementów, dla których ma być przydzielona pamięć
size
Rozmiar (w bajtach) pamięci do zarezerwowania bądź rozmiar pojedynczego elementu
ptr
Wskaźnik zwrócony przez poprzednie wywołanie jednej z funkcji lub NULL

Funkcja calloc przydziela pamięć dla nmeb elementów o rozmiarze size każdy i zeruje przydzieloną pamięć.

Funkcja malloc przydziela pamięć o wielkości size bajtów.

Funkcja free zwalnia blok pamięci wskazywany przez ptr wcześniej przydzielony przez jedną z funkcji malloc, calloc lub realloc. Jeżeli ptr ma wartość NULL funkcja nie robi nic.

Funkcja realloc zmienia rozmiar przydzielonego wcześniej bloku pamięci wskazywanego przez ptr do size bajtów. Pierwsze n bajtów bloku nie ulegnie zmianie gdzie n jest minimum z rozmiaru starego bloku i size. Jeżeli ptr jest równy zero (tj. NULL), funkcja zachowuje się tak samo jako malloc.

Wartość zwracana

edytuj

Jeżeli przydzielanie pamięci się powiodło, funkcje calloc, malloc i realloc zwracają wskaźnik do nowo przydzielonego bloku pamięci. W przypadku funkcji realloc może to być wartość inna niż ptr.

Jeśli jako size, nmeb podano zero, zwracany jest albo wskaźnik NULL albo prawidłowy wskaźnik, który można podać do funkcji free (zauważmy, że NULL jest też prawidłowym argumentem free).

Jeśli działanie funkcji nie powiedzie się, zwracany jest NULL i odpowiedni kod błędu jest wpisywany do zmiennej errno. Dzieje się tak zazwyczaj, gdy nie ma wystarczająco dużo miejsca w pamięci.

Przykład

edytuj
/*UWAGA! Ten kod jest zle napisany, bardzo źle*/
#include <stdio.h>
#include <stdlib.h>
 
int main()
{
    size_t size = 64;
    float tmp, *tab;
 
    // Przydzielenie początkowego bloku pamięci
    tab = malloc(sizeof *tab * (size + 1));
    if (!tab) 
    {
        perror("malloc");
        return EXIT_FAILURE;
    }
    // odwołanie do elementu poza tablicą
    tab[size + 1] = 0;

    puts("Podaj liczbe. Wpisanie nie-liczby zakonczy.");

    // Odczyt liczb
    while (scanf("%f", &tmp) == 1)
    {
        // Jeżeli zapełniono całą tablicę, trzeba ją zwiększyć
        // błędny warunek - prawdziwy tylko gdy size jest równe 0
        if (tab == tab + size) 
        {
            size *= 2;
            float *ptr = realloc(tab, (size + 1) * sizeof *ptr);
            if (!ptr) 
            {
                free(tab);
                perror("realloc");
                return EXIT_FAILURE;
            }
            // znowu odwołanie się do elementu poza tablicą
            ptr[size + 1] = 0;
            tab = ptr;
        }
        *tab = tmp;
        ++tab;
        printf("Podaj liczbe: ");
    }
 
    // Wypisanie w odwrotnej kolejnosci
    while (*tab) 
    {
        --tab;
        printf("%f\n", *tab);
    }
 
    // Zwolnienie pamieci i zakonczenie programu
    free(tab);
    return EXIT_SUCCESS;
}
Podaj liczbe. Wpisanie nie-liczby zakonczy.
10
Podaj liczbe: 15
Podaj liczbe: 30
Podaj liczbe: 35
Podaj liczbe: 70
Podaj liczbe: 75
Podaj liczbe:
75.000000
70.000000
35.000000
30.000000
15.000000
10.000000

Użycie rzutowania przy wywołaniach funkcji malloc, realloc oraz calloc w języku C jest zbędne i szkodliwe. W przypadku braku deklaracji tych funkcji (np. gdy programista zapomni dodać plik nagłówkowy stdlib.h) kompilator przyjmuje domyślną deklaracje, w której funkcja zwraca int. Przy braku rzutowania spowoduje to błąd kompilacji (z powodu niemożności skonwertowania liczby na wskaźnik) co pozwoli na szybkie wychwycenie błędu w programie. Rzutowanie powoduje, że kompilator zostaje zmuszony do przeprowadzenia konwersji typów i nie wyświetla żadnych błędów. W przypadku języka C++ rzutowanie jest konieczne.

Zastosowanie operatora sizeof z wyrażeniem (np. sizeof *tablica), a nie typem (np. sizeof float) ułatwia późniejszą modyfikację programów. Gdyby w pewnym momencie programista zdecydował się zmienić tablicę z tablicy floatów na tablice doubli, musiałby wyszukiwać wszystkie wywołania funkcji malloc, realloc i calloc, co nie jest konieczne przy użyciu operatora sizeof z wyrażeniem.

Ponieważ dla parametru size równego zero funkcja może zwrócić albo wskaźnik różny od wartości NULL albo jej równy, zwykłe sprawdzanie poprawności wywołania poprzez przyrównanie zwróconej wartości do zera może nie dać prawidłowego wyniku.

Zobacz też

edytuj
C/Wskaźniki (dokładne omówienie zastosowania)