Dynamiczny przydział pamięci pozwala uzyskać dostęp do obszaru pamięci na stercie o zadanej wielkości po to aby w tym obszarze przechowywać dane.
Dostęp do tej pamięci możliwy jest przez wskaźnik (adres), który uzyskujemy w wyniku wywołania funkcji malloc()
(nazwa funkcji to skrót od memory allocation). Uzyskany obszar pamięci można traktować jako tablicę o rozmiarze określonym w momencie żądania przydzielenia pamięci, czyli w czasie wykonywania programu. Pozwala to na tworzenie dynamicznych struktur danych w odróżnieniu od typowych tablic w języku C, których rozmiar stały i musi być z góry zadeklarowany w momencie pisania kodu.
Funkcja malloc()
alokuje pamięć, zaś funkcja free()
pozwala zwolnić przydzieloną wcześniej pamięć.
Funkcje te zadeklarowane są w pliku stdlib.h
#include <stdlib.h> void *malloc(int rozmiar); void free(void *wskaznik);
Argumentem funkcji malloc()
jest ilość bajtów pamięci jaką chcemy uzyskać. W celu określenia ilość bajtów potrzebnych do pomieszczenia danych przydatny jest operator sizeof()
, który wyznacza ilość bajtów zajmowanych przez zmienną danego typu. Przykładowo, tablica 100 liczb całkowitych potrzebuje sizeof(int)*100
bajtów.
Wartością zwracaną funkcji malloc
jest adres początku przydzielonej pamięci. Adres ten jest wskaźnikiem typu void*
, jest więc adresem nieokreślonego typu. Chcąc uzyskać wskaźnik określonego przez nas typu (np. wskaźnik do liczb całkowitych int
), należy wartość uzyskaną z malloc
przetransformować za pomocą operatora rzutowania (typ)
. Na przykład, jeżeli potrzebujemy tablicy 100 liczb typu int
to wywołanie funkcji malloc
może wyglądać tak:
int *x = (int*)malloc( 100*sizeof(int) );
Jeżeli operacja alokacji pamięci się nie powiodła zwracana jest wartość NULL
(adres 0).
Przykład
Poniższy program prezentuje w jaki sposób dynamicznie przydzielić pamięć dla n
wartości typu float
. Następnie uzyskana tablica jest wypełniana losowymi wartościami z zakresu od 0 do 1.
#include <stdio.h> #include <stdlib.h> #include <time.h> int main() { int n,i; float *dtab; printf("Podaj rozmiar :"); scanf("%d",&n); /* alokacja pamieci */ dtab = (float *) malloc(n * sizeof(float)); if(dtab == NULL) { printf("Blad przydzialu pamieci\n"); exit(1); } srand(time(0)); /* ustawienie ziarna generatora */ for(i=0; i<n; i++) { dtab[i] = rand()/(RAND_MAX + 1.0); } while(--n >= 0) printf("%.4f\n",dtab[n]; /* zwolnienie pamieci */ free(dtab); }
Napisz program, który odwróci kolejność wszystkich znaków w pliku tekstowym o nazwie input.txt
(plik powinien znajdować się w katalogu roboczym programu).
Plik może zawierać dowolnie długi tekst. Ponieważ nie wiadomo ile znaków umieszczonych jest w pliku, program najpierw wyznacza ilość wszystkich znaków w pliku a następnie tworzy dynamicznie tablicę, o takim rozmiarze aby pomieścić całą zawartość pliku (w tym celu wykorzystaj funkcję malloc
). Gdy cała zawartość pliku trafi do tablicy kolejność elementów zostaje odwrócona a uzyskany wynik jest wypisywany na ekranie.
Dane wejściowe programu: plik tekstowy input.txt
. Zakładamy, że plik znajduje sie w bieżącym katalogu roboczym. W przypadku braku pliku wejściowego wypisywany jest stosowny komunikat błędu i program kończy swoje działanie.
Wynik: zawartość pliku wejściowego wypisana w odwrotnej kolejności
Wskazówka: aby „przewinąć” pozycję w pliku file
i ustawić wskaźnik na jego początku można użyć funkcji fseak(file, 0, 0)
.
Przykładowo, dla pliku wejściowego input.txt
o treści:
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
na ekranie pojawi się taka treść
.tile gnicsipida rutetcesnoc ,tema tis rolod muspi meroL
Zobacz: Struktury danych: lista jednokierunkowa
Struktura dynamiczna, w której :
NULL
k
należy wykonać iterację od głowy (od początku) listy poprzez wszystkie kolejne elementy aż do elementu k
Przykład struktury:
struct osoba { char nazwisko[MAX]; char imie[MAX]; int wiek; struct osoba *nastepny; };
Zaimplementuj prostą bazę danych osobowych składającą się z sekwencji rekordów zawierających dane osobowe: imię, nazwisko i wiek osób umieszczonych w bazie danych. Struktury zawierające dane przechowuj w liście jednokierunkowej. Program po uruchomieniu wypisuje menu z możliwymi akcjami do wykonania po podaniu liczby od 1 do 5:
wyswietl
- wyświetlenie całej bazy danychdodaj
- dodanie nowego rekordu do bazy danych. Program przydziela pamięć dla pojedynczego rekordu, pozwala wypełnić zawartość rekordu i dodaje go do listy.usun
- usunięcie z bazy danych rekordu wskazanego przez użytkownika zamien
- modyfikacja zawartości rekordu wskazanego przez użytkownika zamknij
- zamkniecie programu (opróżnienie bazy danych)Zaimplementuj wszystkie funkcjonalności opisane w powyższym menu. Zadbaj o obsługę sytuacji wyjątkowych, np. gdy podczas usuwania rekordu użytkownik poda niepoprawny numer. Po wykonaniu akcji 1, 2, 3 lub 4 menu ponownie jest wyświetlane i użytkownik ponownie może wykonać jedną z akcji. Wybranie pozycji nr 5 kończy działanie programu.
W zadaniu można skorzystać z implementacji z poprzednich zajęć baza.c, gdzie baza danych realizowana była za pomocą tablicy rekordów a nie za pomocą listy.
Dynamiczny przydział pamięci można użyć do tworzenia złożonych typów danych.
Wykorzystamy ten mechanizm do reprezentowania macierzy posiadającej w
wierszy i k
kolumn według schematu przedstawionego na poniższym rysunku.
Macierz reprezentowana jest przez wskaźnik m
, który jest adresem do tablicy posiadającej w
elementów typu float*
, tzn. elementami tablicy są wskaźniki. Każdy z tych wskaźników pokazuje adres początku tablicy zawierającej k
liczb rzeczywistych typu float
. Tablice te tworzą wiersze macierzy.
Dowolny element macierzy, znajdujący się w i
-tym wierszu i j
-tej kolumnie, dostępny jest jako *(*(m+i)+j)
lub m[i][j]
. Inaczej mówiąc, macierz m
to tablica zawierająca tablice liczb, gdzie m[i]
to adres tablicy zawierającej elementy i
-tego wiersza.
Poniżej znajduje się przykład funkcji, która dla danych liczb całkowitych w
i k
tworzy dynamicznie strukturę macierzy posiadającej w
wierszy i k
kolumn. Funkcja zwraca wskaźnik określający początek macierzy lub wartość NULL
jeśli coś poszło nie tak przy alokacji pamięci.
/* Funkcja rezerwuje pamiec dla macierzy o rozmiarach w * k */ float **utworz_macierz(int w, int k) { float **m; int i; m = (float**)malloc( w * sizeof(float*)); if ( m == NULL ) return NULL; for(i=0; i<w; i++) { m[i] = (float*)malloc( k * sizeof(float)); if( m[i] == NULL ) return NULL; } return m; }
Zaimplementuj program, służący do wykonywania operacji na macierzach, w którym macierze reprezentowane są strukturą zdefiniowaną w poprzednim przykładzie. Zaimplementuj w programie następujące funkcje:
wczytaj_macierz
- funkcja wypełnia macierz wartościami podanymi przez użytkownika. Dla macierzy o wymiarach w
na k
wczytuje w*k
liczb rzeczywistych ze standardowego wejścia i umieszcza je w kolejnych elementach macierzy wyświetl_macierz
- wyświetla macierz na ekranie zwolnij_macierz
- zwalnia pamięć zajętą przez macierz m
oraz ilość wierszy macierzy w
suma_macierzy
- wykonuje operację dodawania dwóch macierzy. m1
, adres macierzy drugiej m2
' oraz rozmiary (liczba wierszy i kolumn) obu macierzy (obie macierze muszą mieć takie same rozmiary). iloczyn_macierzy
- wykonuje operację mnożenia dwóch macierzy. w1
i k1
oraz adres macierzy pierwszej m1
, rozmiary i adres macierzy drugiej w2
, k2
, m2
w1
na k2
stanowiącej iloczyn macierzy pierwszej i drugiej lub wartość NULL
, gdy nie udało się pomnożyć macierzy (np. niezgodne wymiary lub błąd alokacji pamięci) Napisz program, który wczyta 2 macierze o zadanych rozmiarach do pamięci komputera a następnie umożliwi wykonanie operacji dodawania macierzy lub iloczynu macierzy. Po wykonaniu działania wynik prezentowany jest na standardowym wyjściu.
Napisz program, który posortuje dowolnie długą sekwencję liczb rzeczywistych umieszczoną w pliku tekstowym liczby.txt
.
Zakładamy, że plik zawiera wyłącznie liczby (po jednej liczbie w każdej linii). Program powinien pozwalać posortować dowolnie długą sekwencję liczb, dlatego powinien wykorzystywać mechanizm dynamicznego przydziału pamięci, tak aby wszystkie dane pomieścić w odpowiedniej strukturze danych.
Zadanie można zrealizować za pomocą jednego z dwóch podejść:
N
. Ponieważ nie wiadomo jak wiele będzie wartości wejściowych, program najpierw wyznacza wartość N
(ilość liczb w pliku) a następnie, tworzy dynamicznie tablicę o takim rozmiarze aby pomieścić wszystkie liczby zawarte w pliku, po czym dane są wczytywane do tablicy. Następnie wypełniamy tablicę wartościami z pliku i sortujemy zawartość tablicy. file
i ustawić wskaźnik na jego początku można użyć funkcji fseak(file, 0, 0)
.
Dane wejściowe programu: plik tekstowy liczby.txt
zawierający dowolnie długą serię liczb rzeczywistych. Zakładamy, że plik znajduje sie w bieżącym katalogu roboczym. W przypadku braku pliku wejściowego wypisywany jest stosowny komunikat błędu i program kończy swoje działanie.
Wynik: ciąg wszystkich liczb z pliku wejściowego wypisany w kolejności od najmniejszej do największej wartości
Przykładowy plik danych wejściowych: liczby.txt
Rozwiązanie umieść w Moodle pod adresem https://moodle.umk.pl/WFAIIS/mod/assign/view.php?id=7608