Temat: Dynamiczne przydzielanie i zwalnianie pamięci. Struktura listy

Transkrypt

Temat: Dynamiczne przydzielanie i zwalnianie pamięci. Struktura listy
Temat: Dynamiczne przydzielanie i zwalnianie pamięci.
Struktura listy – operacje wstawiania,
wyszukiwania oraz usuwania danych.
1. Rodzaje pamięci używanej w programach
Pamięć komputera, dostępna dla programu, dzieli się na cztery obszary:
• kod programu,
• dane statyczne ( np. stałe i zmienne globalne programu),
• dane automatyczne (zmienne lokalne funkcji - tworzone i
usuwane automatycznie przez kompilator) → tzw. STOS (ang.
stack)
• dane dynamiczne (zmienne, które można tworzyć i usuwać w
dowolnym momencie pracy programu) → w pamięci wolnej
komputera → tzw. STERTA (ang. heap)
Zmienne dynamiczne
→
są to zmienne tworzone przez programistę
w pamięci wolnej komputera (na stercie)
→
dostęp do takiej zmiennej możliwy jest
jedynie poprzez jej adres w pamięci
(przechowywany
w
zmiennej
wskaźnikowej).
2. Funkcje przydziału i zwalniania pamięci
W języku C do dynamicznego przydzielania pamięci (tworzenia
zmiennych dynamicznych) służą specjalne funkcje z biblioteki < alloc.h >
albo <stdlib.h>.
void ∗malloc( size_t rozmiar );
// przydział bloku o zadanej wielkosci
void ∗calloc( size_t il_elementow, size_t rozmiar); // przydział pamięci
// dla tablicy dynamicznej
void free( void ∗wskaznik);
unsigned coreleft( void );
// zwolnienie wskazywanego obszaru
// sprawdzenie wolnego miejsca na stercie
1
Przykład 1
// przydział i zwolnienie pamięci na stercie
int main( )
{
int ∗wsk;
// zmienna wskaźnikowa do zapamiętania adresu liczby int
int x=5;
• • •
wsk = (int∗) malloc( sizeof(int) );
// przydzielenie pamięci na liczbę int
if( wsk == NULL )
printf( ”Błąd przydziału pamięci” );
else
{
∗wsk = 10;
∗wsk ∗= 2;
printf( ”%d”, ∗wsk );
scanf( ”%d”, wsk );
// przykładowe operacje na dynamicznej liczbie int
• • •
free( wsk );
• • •
// zwolnienie pamięci
// dane spod wskaźnika wsk już nie są dostępne, a
// dane ze zmiennej x są w dalszym ciągu widoczne
}
return 0;
}
wsk
20
x
Stack (stos)
Heap (sterta)
2
Przykład 2
// tablica dynamiczna
int rozmiar_tablicy;
double ∗T;
printf( ”Ile liczb chcesz wprowadzić: ” );
scanf( ”%d”, &rozmiar_tablicy );
if( T = (double∗) calloc( rozmiar_tablicy, sizeof(double) ) ) // przydział
// pamięci dla tablicy dynamicznej
{
for( int i = 0; i < rozmiar_tablicy, i++ );
∗( T+i ) = i*2; // albo T[ i ] =i*2;
• • •
free(T); // zwolnienie pamięci przeznaczonej na tablicę
}
Przykład 3
// struktura dynamiczna
struct Osoba{
char nazwisko[30];
char imie[20];
float zarobki;
};
Osoba ∗wsk_osoby;
wsk_osoby = (Osoba∗) malloc( sizeof(Osoba) );
// przydział pamięci dla
// danych typu Osoba. Dane są dostępne przez wskaźnik wsk_osoby
if( wsk_osoby )
{
printf( ”Podaj nazwisko: ” );
gets(wsk_osoby −> nazwisko );
printf( ”Podaj imie: ” );
gets(wsk_osoby −> imie);
printf( ”Podaj zarobki: ” );
scanf(„%f”,&(wsk_osoby −> zarobki));
// if( wsk_osoby != NULL )
...
Wsk_osoby->zarobki+=100.50;
...
free( wsk_osoby ); // zwolnienie pamięci przeznaczonej dla danych
// typu Osoba
}
Jeżeli pod wskaźnikiem zapamiętana jest struktura, to dostęp do jej pól
jest możliwy przez użycie operatora „->”
3
Przykład 4
//dynamiczna tablica struktur
int rozmiar_tablicy;
Osoba ∗T;
printf( ”Ile liczb chcesz wprowadzić: ” );
scanf( ”%d”, &rozmiar_tablicy );
T = (Osoba∗) calloc( rozmiar_tablicy, sizeof(Osoba));
// przydział pamięci dla tablicy dynamicznej
if( T == NULL )
printf( ”Błąd przydziału pamięci” );
else
for( int i = 0; i < rozmiar_tablicy, i++ );
{
printf( ”Podaj nazwisko: ” );
gets(( T+i ) −> nazwisko ); // gets(T[i].nazwisko);
printf( ”Podaj imie: ” );
gets((T+i)->imie); // gets(T[i].imie);
• • •
printf(„Podaj zarobki:”);
scanf(„%f”,&((T+i)->zarobki)); // scanf(„%f”,&T[i].zarobki);
}
• • •
free( T );
dynamicznej
// zwolnienie pamięci przeznaczonej dla tablicy
3. Struktura listy (jednokierunkowej)
Struktura listy, to liniowa struktura dynamiczna, dla której:
nie trzeba podawać maksymalnej liczby danych,
można wstawiać, usuwać i wyszukiwać dane,
w trakcie usuwania danych listy zwalniana jest pamięć, której zasób
można ponownie wykorzystać w programie.
Listę budują wskaźniki do struktury, w których zdefiniowane są dwa pole:
pole danych,
pole z adresem kolejnego elementu (wskaźnika) listy
Listę reperezentuje adres jej pierwszego elementu.
4
Przykładowa deklaracja wskaźnika reperezentującego element listy
struct Osoba{
nazwisko
char nazwisko[40];
dane
wiek
unsigned wiek;
float zarobki;
zarobki
next
};
struct Lista{
Osoba dane;
Lista *next; };
Pierwszy
Kowalski,80,
dane
1200.00
next
Nowak,
28,
dane
1123,5
next
Iksinski
1300.45
32,
dane
next
NULL
a) Operacja dodawania nowego elemenu na koniec listy
Przed
Po
Pierwszy
Ostatni
NULL
Ostatni
NULL
Pierwszy – adres pierwszego elementu listy
Ostatni – adres ostatniego elementu listy
O – struktura z danymi, które wstawiamy na koniec listy
Adresy Pierwszy i Ostatni mogą się zmieniać w trakcie realizacji funkcji
dodaj_osobe, dlatego są przekazywane przez adres (stąd dwie gwiazdki
przed nazwą parametru)
Jedno wywołanie funkcji dodaj_osobe dodaje na koniec listy jeden element.
Dwa przypadki realizacji funkcji dodaj_osobe:
wstawianie do pustej listy,
wstawianie do niepustej listy
void dodaj_osobe(Lista **Pierwszy,Lista **Ostatni,Osoba O)
{
Lista *pomoc;
pomoc=(Lista *)malloc(sizeof(Lista)); // rezerwacja pamięci
pomoc->dane=O; // kopiowanie danych ze struktury O do pola dane
// wskaźnika pomoc
pomoc->next=NULL; // ustawienie wskaźnika next w adresie pomoc na
// wartości NULL
if (*Pierwszy==NULL) // przypadek wstawiania do pustej listy
*Pierwszy=pomoc;
else (*Ostatni)->next=pomoc; // przypadek wstawiania do niepustej listy
// „podczepienie” wskaźnika pomoc
*Ostatni=pomoc;
// zmiana wskaźnika Ostatni
}
b) Operacja przeglądania listy
Funkcja szukaj sprawdza, czy dane parametrem naz znajduje się na liście.
Jeżeli tak, to wynikiem funckji szukaj jest wartości 1, w przeciwnym przypadku
zwracana jest wartość 0.
Pierwszy – adres pierwszego elementu na liście
naz – poszukiwane nazwisko
Lista * szukaj(Lista *Pierwszy,char *naz)
{
Lista *pomoc;
Lista* wynik=NULL;
pomoc=Pierwszy; // ustawienie wskaźnika pomoc na pierwszym elemencie
// listy
while (pomoc) // pętla przglądająca listę
{
if (strcmp(pomoc->dane.nazwisko,naz)==0)
{
wynik=pomoc;
break;
}
pomoc=pomoc->next; // ustawienie wskaźnika pomoc na kolejnym
// elemencie listy
}
return wynik;
}
int policz_bogaczy(Lista *Pierwszy,float kwota)
{
Lista *pomoc;
int ile=0;
pomoc=Pierwszy; // ustawienie wskaźnika pomoc na pierwszym elemencie
// listy
while (pomoc) // pętla przglądająca listę
{
If (pomoc->dane.zarobki>kwota) ile++;
pomoc=pomoc->next; // ustawienie wskaźnika pomoc na kolejnym
// elemencie listy
}
return ile;
}
c) Operacja usuwania elementu listy
Przed
Po
Pierwszy
Element do
usunięcia
Pierwszy
Funkcja usun_osobe usuwa osobę o podanym nazwisku. Jeżeli na liście
występuje kilka osób o podanym do usunięcia nazwisku, to usuwane są
wszystkie elementy z tym nazwiskiem.
Pierwszy – adres pierwszego elementu listy
naz – nazwisko do usunięcia
Funkcja zwraca wartość 1, gdy usunięcie zaszło i wartość 0 w przeciwnym
przypadku.
Przypadki realizacji funkcji usun_osobe:
nazwiska naz nie ma na liście,
nazwisko naz występuje w adresie Pierwszy
nazwisko naz występuje w adresie innym niż Pierwszy
int usun_osobe(Lista **Pierwszy,char *naz)
{
int byla=0;
Lista *pomoc,*poprzedni; // wskaźnik poprzedni ustawiany jest na elemencie
// poprzednim w stosunku do usuwanego
pomoc=*Pierwszy;
poprzedni=NULL;
while (pomoc!=NULL)
{
if (strcmp(pomoc->dane.nazwisko,naz)==0)// nazwisko naz znajduje się w
// adresie pomoc
{
byla=1;
if (pomoc==*Pierwszy) { // nazwisko naz występuje w adresie
// Pierwszy
*Pierwszy=pomoc->next;
free(pomoc);
pomoc=*Pierwszy;
}
else { // nazwisko naz znajduje się adresie innym niż Pierwszy
poprzedni->next=pomoc->next;
free(pomoc);
pomoc=poprzedni->next;
}
break;
}
else { // nazwiska naz nie ma w adresie pomoc i trzeba przejść
// z adresami poprzedni i pomoc do następnego elementu listy
poprzedni=pomoc;
pomoc=pomoc->next;
}
}
return byla;
}
Przykład 5
// Program z wykładu nr 10, wktórym tablica struktur została zastąpiona
// strukturą listy
#include <stdio.h>
#include <conio.h>
#include <stdlib.h>
#include <string.h>
struct Osoba {
char nazwisko[40];
unsigned wiek;
float zarobki;
};
struct Lista{
Osoba dane;
Lista *next;
};
// ---------------- wczytywanie danych z klawiatury---------------------------------Osoba wczytaj_dane(){
Osoba O;
printf("Nazwisko:");
scanf("%s",O.nazwisko);
printf("Wiek:");
scanf("%d",&O.wiek);
printf("Zarobki:");
scanf("%f",&O.zarobki);
return O;
}
// ---------------------------dodawanie osoby na koniec listy--------------------void dodaj_osobe(Lista **Pierwszy,Lista **Ostatni,Osoba O)
{
Lista *pomoc;
pomoc=(Lista *)malloc(sizeof(Lista));
pomoc->dane=O;
pomoc->next=NULL;
if (*Pierwszy==NULL)
*Pierwszy=pomoc;
else (*Ostatni)->next=pomoc;
*Ostatni=pomoc;
}
//---------usuwanie podanej osoby z listy -------------------------------------------------short usun_osobe(Lista **Pierwszy,char *naz)
{
short byla=0;
Lista *pomoc,*poprzedni;
pomoc=*Pierwszy;
poprzedni=NULL;
while (pomoc!=NULL)
{
if (strcmp(pomoc->dane.nazwisko,naz)==0)
{
byla=1;
if (pomoc==*Pierwszy) {
*Pierwszy=pomoc->next;
free(pomoc);
pomoc=*Pierwszy;
}
else {
poprzedni->next=pomoc->next;
free(pomoc);
pomoc=poprzedni->next;
}
break;
}
else {
poprzedni=pomoc;
pomoc=pomoc->next;
}
}
return byla;
}
//-------------- wypisywanie danych na ekranie --------------------------------------------void wypisz_informacje(Osoba O)
{
printf("Nazwisko:%s\nWiek:%d\nZarobki:%0.2f\n",O.nazwisko,
O.wiek,O.zarobki);
}
//-----------------szukanie osoby i wypisywanie jej danych na ekranie -------------int szukaj(Lista *Pierwszy,char *naz)
{
Lista *pomoc;
int byla=0;
pomoc=Pierwszy;
while (pomoc!=NULL)
{
if (strcmp(pomoc->dane.nazwisko,naz)==0)
{
wypisz_informacje(pomoc->dane);
byla=1;
}
pomoc=pomoc->next;
}
return byla;
}
// -------------- wyznaczanie liczby osób, które zarabiają powyzej zar---------------int policz_bogaczy(Lista *Pierwszy,float zar)
{
int l=0;
Lista *pomoc;
pomoc=Pierwszy;
while (pomoc!=NULL)
{
if (pomoc->dane.zarobki>zar)
l++;
pomoc=pomoc->next;
}
return l;
}
// ------------ menu programu ----------------------------------------------int menu()
{
int z;
printf("1-dodaj osobe\n");
printf("2-usun osobe\n");
printf("3-znajdz osobe\n");
printf("4-policz bogaczy\n");
printf("5-zapisz dane\n");
printf("6-wczytaj dane\n");
printf("7-drukuj w pliku\n");
printf("8-wyswietl na ekranie\n");
printf("9-koniec programu\n");
scanf("%d",&z);
return z;
}
// ---------------zapis danych z listy do pliku binarnego ---------------------void zapis(char *sciezka,Lista *Pierwszy)
{
FILE *f;
Lista *pomoc;
pomoc=Pierwszy;
f=fopen(sciezka,"wb");
if (f!=NULL)
while (pomoc!=NULL)
{
fwrite(&(pomoc->dane),sizeof(Osoba),1,f);
pomoc=pomoc->next;
}
fclose(f);
}
// ------------- odczyt danych z pliku binarnego i wstawienie danych na listę-----void odczyt(char *sciezka,Lista **Pierwszy,Lista **Ostatni)
{
FILE *f;
Lista *pomoc;
f=fopen(sciezka,"rb");
pomoc=*Pierwszy;
Osoba O;
if (f!=NULL)
while (fread(&O,sizeof(Osoba),1,f)==1)
dodaj_osobe(Pierwszy,Ostatni,O);
fclose(f);
}
//----------------zapis danych z listy w pliku tekstowym --------------------void zapis_do_tekstowego(char *sciezka,Lista *Pierwszy)
{
FILE *f;
Lista *pomoc;
int i=0;
f=fopen(sciezka,"wt");
pomoc=Pierwszy;
if (f!=NULL)
{
while (pomoc!=NULL)
{
if (i==0)
fprintf(f,"Lista osob\n");
fprintf(f,"%40s %3d %10.2f\n",pomoc->dane.nazwisko,
pomoc->dane.wiek,pomoc->dane.zarobki);
pomoc=pomoc->next;
i++;
}
}
fclose(f);
}
// ---------------------- wyświetlenie danych z listy na ekranie ---------------void wyswietl_dane(Lista *Pierwszy)
{
Lista *pomoc=Pierwszy;
while (pomoc!=NULL)
{
wypisz_informacje(pomoc->dane);
pomoc=pomoc->next;
}
}
//------------------------usuwanie całej listy-----------------------------void usun_liste(Lista **Pierwszy)
{
Lista *pomoc=*Pierwszy;
while (pomoc!=NULL)
{
*Pierwszy=pomoc->next;
free(pomoc);
pomoc=*Pierwszy;
}
}
// -----------------------funkcja main ----------------------------------------------int main( )
{
Lista *Pierwszy,*Ostatni;
Pierwszy=NULL;
Ostatni=NULL;
char naz[40];
int z,byla,w;
float zar;
Osoba O;
char sciezka[100];
do {
z=menu();
switch(z)
{
case 1: O=wczytaj_dane();dodaj_osobe(&Pierwszy,&Ostatni,O);break;
case 2: printf("Podaj nazwisko do usuniecia:");
scanf("%s",naz);
byla=usun_osobe(&Pierwszy,naz);
if (!byla) printf ("Brak takiej osoby !!!\n");
break;
case 3:printf("Podaj nazwisko osoby szukanej: ");
scanf("%s",naz);
byla=szukaj(Pierwszy,naz);
if (!byla) printf("Brak osoby szukanek !!!\n");
break;
case 4:printf("Podaj wysokosc sredniej krajowej: ");
scanf("%f",&zar);
printf("Liczba osob zarabiajacych powyzej
sredniej:%d\n",policz_bogaczy(Pierwszy,zar));
break;
case 5:printf("Podaj sciezke pliku do zapisu: ");
scanf("%s",sciezka);
zapis(sciezka,Pierwszy);
break;
case 6:printf("Podaj sciezke pliku do odczytu: ");
scanf("%s",sciezka);
if (Pierwszy!=NULL)
{
printf("Zestaw danych nie jest pusty, co dalej?\n 1- dopisac dane z
pliku, 2-nadpisac dane ?:");
scanf("%d",&w);
if (w==2) {
usun_liste(&Pierwszy);
Ostatni=NULL;
}
}
odczyt(sciezka,&Pierwszy,&Ostatni);
break;
case 7:printf("Podaj sciezke pliku tekstowego: ");
scanf("%s",sciezka);
zapis_do_tekstowego(sciezka,Pierwszy);
break;
case 8:wyswietl_dane(Pierwszy);
break;
}
}while (z!=9);
return 0;
}