Zmienne i struktury dynamiczne

Transkrypt

Zmienne i struktury dynamiczne
Zmienne i struktury dynamiczne
Zmienne dynamiczne są to zmienne, które tworzymy w trakcie działania programu za pomocą
operatora new. Usuwa się je operatorem delete. Czas ich występowania w programie jest
uzależniony od potrzeb programu, zajmują więc miejsce w pamięci tylko wówczas, gdy są
wykorzystywane (zmienne statyczne istnieją od początku do końca trwania programu).
Zmienna dynamiczna nie ma własnej nazwy, dlatego dostęp do niej jest możliwy wyłącznie przez
adres obszaru pamięci, który ona zajmuje. Adres ten jest przechowywany we wskaźniku do tej
zmiennej. Przy deklaracji wskaźnika, dobrym zwyczajem jest nadanie mu wartości początkowej
NULL (adres zerowy).
Tablica dynamiczna jednowymiarowa
Def.
Tablica dynamiczna to taka tablica, która zostaje utworzona w trakcie działania programu i może
być usunięta przed jego zakończeniem.
Tablice dynamiczne mogą przechowywać elementy jednego typu (podobnie statyczne). Aby móc
zadeklarować tablicę, musimy najpierw zadeklarować wskaźnik zdolny wskazywać element tablicy,
a następnie za jego pomocą dynamicznie alokować pamięć przeznaczoną na przechowywanie tego
elementu. Na żądanie napisanego przez nas programu system operacyjny przyznaje nam obszar
pamięci, w którym będą przechowywane dane, w takiej ilości, w jakiej jest nam aktualnie
potrzebne. Uzyskane w ten sposób dynamicznie alokowane obszary pamięci należy zwolnić przed
zakończeniem działania programu.
Deklaracja dynamicznej tablicy – przykład:
float *tab = NULL;
tab = new float[k];
//na razie wskaźnik na nic nie wskazuje
//k – rozmiar tablicy (może być wpisany z klawiatury)
Powyższe instrukcje można połączyć:
float *tab = new float[k];
Przykład:
Program pyta użytkownika, ile liczb chce wprowadzić do tablicy, następnie wypełnia tablicę
podanymi liczbami i wyświetla je.
#include <iostream>
#include <iomanip>
#include <cstdio>
#include <new>
using namespace std;
int main()
{
float *tab = NULL;
// dołączamy bibliotekę w której zdefiniowany
// jest sposób postępowania w razie braku
// miejsca na utworzenie tablicy
// deklaracja wskaźnika i ustawienie jego
// wartosci na adres zerowy czyli NULL
cout << "Ile liczb wpiszesz do tablicy? ";
int ile;
float liczba;
cin >> ile;
try
// próbujemy powołać do życia nową tablicę
{
// za pomocą operatora new
tab = new float [ile];
}
catch(bad_alloc)
// jesli nie udało sie utworzyć
{
// tablicy z powodu braku wolnej pamieci
cout << "Brak miejsca na utworzenie tablicy";
cin.ignore();
getchar();
return -1;
}
for (int i=0; i<ile; i++)
// jeśli utworzenie powiodło się
{
cout << "Podaj liczbe: ";
cin >> liczba;
*(tab+i) = liczba;
//
}
cout << endl << "Wypisuje zawartosc
for (int i=0; i<ile; i++)
cout << setw(6) << *(tab+i);
delete [] tab;
//
cin.ignore();
//
getchar();
return 0;
wypełniamy komórkę tablicy
tablicy:" << endl;
usuwamy tablicę z pamięci
za pomocą operatora delete
}
Jeśli w programie, korzystając z zapasu pamięci, zamierzamy utworzyć bardzo dużą tablicę,
wówczas możemy nie znaleźć wystarczająco dużego, spójnego obszaru na jej przechowanie.
W takim wypadku wskaźnik tab zamiast wskazywać na początek tablicy, wskazywał będzie na
adres NULL.
W bloku try umieszczamy funkcje, które chcemy śledzić pod kątem wystąpienia błędów.
W naszym programie jest to błąd związany z nieudaną alokacją pamięci za pomocą operatora new.
Instrukcja catch przechwytuje przypadki, kiedy funkcja wewnątrz bloku try się nie powiodła.
W programie nie przesuwamy wskaźnika tab, a jedynie wpisujemy (lub wypisujemy) liczby do
komórek odpowiednio przesuniętych względem komórki, na którą wskazuje wskaźnik tab.
Jeśli przypadkową instrukcją przypisania zmienisz wartość wskaźnika wskazującego na tablicę
dynamiczną, możesz stracić możliwość korzystania z tej tablicy.
Po instrukcji zwolnienia pamięci zajętej przez tablicę wskaźnik tab nadal istnieje, ponieważ jest
on zmienną statyczną, którą nie da się usunąć z programu.
Tablica dynamiczna dwuwymiarowa
Dwuwymiarowa tablica dynamiczna będzie jednowymiarową tablicą dynamiczną, przechowującą
wskaźniki do tablic dynamicznych jednowymiarowych, w których umieszczone zostaną dane.
Deklaracja dwuwymiarowej tablicy dynamicznej [w][k] – przykład:
int
**tab;
tab = new int *[w];
//tab jest wskaźnikiem do wskaźnika na zmienne typu int
//w – liczba wierszy; tablica jednowymiarowa wskaźników
//do zmiennych typu int
for (i=0; i<w; i++)
//dla każdego wskaźnika z poprzedniej tablicy
tab[i] = new int [k]; //tworzymy tablicę liczb całkowitych; k – liczba kolumn
Przykład:
Napiszemy program, który utworzy dwuwymiarową tablicę dynamiczną o żądanych wymiarach
(podanych z klawiatury) i wypełni ją losowymi liczbami z przedziału <0, 100>.
#include <iostream>
#include <iomanip>
#include <cstdio>
#include <cstdlib>
#include <new>
using namespace std;
int main()
{
int wie, kol, i, j;
cout << "Podaj liczbe wierszy i kolumn tablicy:\n";
cin >> wie >> kol;
srand(time(NULL));
// inicjacja generatora liczb
int **tab;
// deklaracja wskaźnika do wskaźnika na zmienne typu int
try
// próba tworzenia tablicy wskaźników
{
tab = new int *[wie];
}
catch(bad_alloc)
{
cout << "Brak miejsca na utworzenie tablicy. Koncze program";
return -1;
}
for (i=0; i<wie; i++)
try
// próba tworzenia dynamicznych tablic liczb
{
tab[i] = new int [kol];
}
catch(bad_alloc)
{
cout << "Brak miejsca na utworzenie tablicy. Koncze program";
return -1;
}
for (i=0; i<wie; i++)
for (j=0; j<kol; j++)
{
tab[i][j] = rand()%101;
// wypełnianie tablic liczbami
cout << setw(4) << tab[i][j]; // wyświetlanie elementów
}
cout << endl;
// teraz usuniemy tablicę z obszaru zajmowanej pamieci
for (i=0; i<wie; i++)
delete [] tab[i];
// usuwamy kolejne tablice z liczbami
delete [] tab;
// usuwamy tablicę wskaźników
cin.ignore();
getchar();
return 0;
}
Poszczególne tablice z danymi nie muszą mieć takiego samego rozmiaru.
Jak to jest w tym przykładzie?
Przykład:
Napiszemy program, który utworzy tabelę odległości pomiędzy miastami. Nazwy miast oraz
odległości pomiędzy nimi podaje użytkownik.
#include <iostream>
#include <iomanip>
#include <cstdio>
#include <new>
using namespace std;
int main()
{
int liczba, i, j;
// liczba – zmienna przechowująca liczbę miast
cout << "Podaj liczbe miast: ";
cin >> liczba;
char **miasta;
// deklarujemy tablicę dwuwymiarową o nazwie miasta
miasta = new char *[liczba]; // w której poszczególne wiersze zawierają
for (i=0; i<liczba; i++)
// nazwy miast (będące tablicami znaków)
miasta[i] = new char[20];
for (i=0; i<liczba; i++)
{
cout << "Podaj nazwe miasta: ";
cin >> miasta[i];
}
int **tab;
// deklarujemy tablicę odległości między miastami
tab = new int *[liczba];
for (i=0; i<liczba; i++)
try
// próba utworzenia tablicy
{
tab[i] = new int[i+1];
}
catch(bad_alloc)
// jeśli brak miejsca na tablicę
{
return -1;
// to zakończ działanie programu
}
for (i=0; i<liczba; i++)
for (j=0; j<i+1; j++)
if (i==j)
tab[i][j]=0;
else
{
cout << "Podaj odleglosc z miasta " << miasta[i];
cout << " do miasta " << miasta[j] << ": ";
cin >> tab[i][j];
}
cout << "
";
for (i=0; i<liczba; i++)
cout << setw(10) << miasta[i];
cout << endl;
for (i=0; i<liczba; i++)
{
for (j=0; j<i+1; j++)
{
if (j==0)
cout << setw(10) << miasta[i];
cout << setw(10) << tab[i][j];
}
cout << endl;
}
for (i=0; i<liczba; i++)
delete [] tab[i];
delete [] tab;
delete [] miasta;
cin.ignore();
getchar();
return 0;
}
Lista jednokierunkowa – tworzenie listy i wprowadzanie do niej elementów
Dynamiczna struktura danych jest ciągiem powiązanych ze sobą zmiennych dynamicznych, których
liczba nie jest znana w czasie kompilacji, a nawet podczas uruchamiania programu.
Lista jest złożoną dynamiczną strukturą danych, która przechowuje dane wraz z zapamiętaniem ich
kolejności. W przeciwieństwie do tablicy elementy listy nie muszą być położone w sąsiednich
komórkach pamięci.
Lista jest dobrym rozwiązaniem, gdy chcemy np. zapamiętać dużą liczbę elementów, a nie
dysonujemy wystarczająco dużym spójnym obszarem na utworzenie tablicy dynamicznej.
Jednak główną i najważniejszą cechą listy jest możliwość dołączania elementów bez zdefiniowania
uprzednio ich liczby. Nawet w przypadku tablicy dynamicznej po podaniu rozmiaru nie możemy
już jej powiększyć.
Tworzenie listy zaczynamy od zdefiniowania jej pierwszego elementu, czyli głowy. Głowa jest
jednocześnie identyfikatorem listy. Do głowy dołączamy następne elementy. Dla każdego
kolejnego elementu listy będzie znajdowany wolny obszar pamięci, do którego zostaną wpisane
dane z nim związane. W wypadku listy jednokierunkowej każdy element zna adres elementu
następnego, natomiast nie zna adresu elementu poprzedniego.
Elementami listy są struktury. Każda ze struktur musi mieć, oprócz pól z danymi, dodatkowe pole,
w którym jest przechowywany wskaźnik do następnego elementu listy. Ostatni element listy
wskazuje na adres zerowy, niezwiązany z żadnym obiektem (NULL) – informacja, że w liście nie
ma już więcej elementów.
Lista jest strukturą o dostępie sekwencyjnym, co oznacza, że aby dostać się np. do czwartego
elementu, należy „przejść” wszystkie wcześniejsze elementy, począwszy od głowy. Natomiast
w tablicy mamy do czynienia z dostępem swobodnym, czyli wystarczy podać indeks elementu, aby
uzyskać do niego dostęp.
W listach mamy dwa zasadnicze sposoby dołączania kolejnych elementów: dołączanie do początku
listy i dołączanie do końca listy.
/Który ze sposobów jest użyty w poniższym przykładzie?/
Przykład:
Napiszemy program, który zadaje pytanie, czy chcemy dodać element do listy. Dopóki otrzymuje
pozytywną odpowiedź, pobiera wpisaną na klawiaturze liczbę całkowitą i dołącza ją do listy. Na
koniec wyświetla na ekranie monitora całą zawartość listy.
#include <iostream>
#include <cstdio>
#include <new>
using namespace std;
struct element_listy
{
int liczba;
element_listy *nastepny;
};
int main()
{
element_listy *glowa = NULL;
// tworzymy listę pustą
element_listy *nowy;
char odp;
int d;
cout << "Czy chcesz podac element listy? t/n: ";
cin >> odp;
while (odp!='n')
// dołaczamy kolejne elementy
{
cout << "Podaj dane : ";
cin >> d;
try
// próba dołaczenia nowego elementu
{
nowy = new element_listy;
}
catch(bad_alloc)
// jeśli nie ma miejsca na nowy element
{
cout << "Nie ma juz miejsca na nastepny element";
break;
}
nowy->nastepny = glowa;
// jest miejsce na nowy element
nowy->liczba = d;
glowa = nowy;
cout << endl << "Chcesz podac kolejny element listy? t/n: ";
cin >> odp;
}
element_listy *temp = glowa;
// początek wypisywania elementów listy
cout << endl << "Oto utworzona przez ciebie lista:" << endl;
while (temp!=NULL)
{
cout << temp->liczba << " ";
temp = temp->nastepny;
}
cin.ignore();
getchar();
return 0;
}
Przykład:
Pobieranie liczb jak wyżej i dołączanie kolejnych elementów do końca listy.
#include <iostream>
#include <cstdlib>
#include <new>
using namespace std;
struct element_listy
{
int liczba;
element_listy *nastepny;
};
int main()
{
element_listy *glowa = NULL;
element_listy *ogon = NULL;
element_listy *nowy;
char odp;
int d;
cout << "Czy chcesz podac element listy? t/n: ";
cin >> odp;
while (odp!='n')
{
cout << "Podaj dane: ";
cin >> d;
try
{
nowy = new element_listy;
}
catch(bad_alloc)
{
cout << endl << "Brak pamieci na ostatni element";
break;
}
nowy->nastepny = NULL;
nowy->liczba = d;
if (glowa==NULL)
{
glowa = nowy;
ogon = glowa;
}
else
{
ogon->nastepny = nowy;
ogon = nowy;
}
cout << endl << "Czy chcesz podac kolejny element listy? t/n: ";
cin >> odp;
}
element_listy *temp=glowa;
// początek wypisywania elementów listy
cout << endl << "Oto utworzona przez ciebie lista:" << endl;
while (temp!=NULL)
{
cout << temp->liczba << " ";
temp = temp->nastepny;
}
cin.ignore();
getchar();
return 0;
}

Podobne dokumenty