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; }