Wstęp do informatyki Wykład 7 Prosty schemat organizacji pamięci

Transkrypt

Wstęp do informatyki Wykład 7 Prosty schemat organizacji pamięci
Wstęp do informatyki
Wykład 7
Prosty schemat organizacji pamięci operacyjnej podczas działania programu.
program źródłowy -> translator -> program wynikowy
dane -> program wynikowy -> wyniki
W uproszczonym schemacie organizacji pamięci operacyjnej wyróżniamy m.in.
• segment (kodu) programu – stąd pobiera się rozkazy do wykonania. Rozkazy mają odpowiednie adresy w pamięci.
• segment danych (zmienne globalne).
• stos, ang. stack (miejsce na zmienne lokalne obsługiwane na zasadzie stosu)
• sterta lub skład, ang. heap (miejsce na zmienne dynamiczne)
Ilustrowanie działania procedur rekurencyjnych
1. Ilustracja polegająca na powielaniu treści procedury z parametrami aktualnymi zamiast formalnych
Przykładowa procedura rekurencyjna licz (z rekurencją końcową) i ilustracja jej działania
void licz(int n)
{ if (n>=1)
{ printf("%d",n);
licz(n-1);
}
}
Ilustracja wykonywania instrukcji licz(3) przez powielanie treści procedury z aktualnymi parametrami wywołania.
Dojście sterowania do ostatniego nawiasu podczas wykonania procedury oznacza powrót z treści procedury w miejsce
wywołania tej procedury (dokładniej w miejsce tuż po instrukcji wywołania tej procedury).
Podczas wycofywania się z rekurencji w tej procedurze nie są wykonywane żadne obliczenia.
{if (3>=1)
{printf("%d",3);
licz(2);
}
{if (2>=1)
{printf("%d",2);
licz(1);
}
itd.
2. Ilustracja wykonania procedury rekurencyjnej licz1 (bez rekurencji końcowej) z wykorzystaniem stosu
(zob. np. Struktury danych w języku C, Adam Dryzek, Donald L. Simon, WNT, Warszawa, 1996.)
Symboliczne adresy przekładu funkcji są oznaczane przez ai, i=1, 2, 3, 4.
symboliczne adresy przekładu
void licz1(int n)
{
/*a1*/
if (n>=1)
{
/*a2*/
licz1(n–1);
/*a3*/
printf(''%d",n);
}
/*a4*/
}
/*ostatni nawias w funkcji*/
wywołanie
/*a5*/
licz1(3);
/*a6*/
•
•
Po wywołaniu funkcji, np. /*a5*/ licz1(3); /*a6*/ ... na szczyt stosu jest zapisywany rekord wywołania (ang.
activation record) lub ramka stosu (ang. stack frame) zawierający: " wartość parametru aktualnego i adres
powrotu", czyli “3,a6”. Liczba 3 na szczycie stosu jest wartością lokalnej zmiennej n. Przy kolejnych wywołaniach
rekurencyjnych jest podobnie.
Po dojściu sterowania do końca przekładu treści procedury licz1 (ostatniego nawiasu w kodzie źródłowym funkcji),
następuje zdjęcie rekordu wywołania ze stosu i przejście do wykonywania instrukcji o adresie wskazywanym przez
"adres powrotu"; np. jeśli na szczycie stosu był rekord "2,a3”, to ten rekord jest usuwany ze stosu i program
rozpoczyna działanie od instrukcji o adresie a3 (dla printf(...n) wartość n jest na szczycie stosu).
Wywołanie
3
3
licz1(3);
a6
Wywołanie
licz1(2);
a6
2
2
a3
Wywołanie
licz1(1);
a3
1
1
a3
Wywołanie
licz1(0);
a3
0
a3
Przykładowa funkcja rekurencyjna silnia (z adresami przekładów jej fragmentów) i ilustracje jej działania
long silnia(int n)
{
/*a1*/
if (n==0)
/*a2*/
return 1;
else
/*a3*/
return n * silnia( n-1);
}
int main()
{...
/*a4*/ y=silnia(2);
...}
3. Ilustracja obliczania wartości funkcji rekurencyjnej silnia (bez stosu).
Obliczanie wartości wyrażenia silnia(2)
(podkreślone są wywołania rekurencyjne)
silnia(2)
2
--------↑
2*silnia(1)
2*1=2
--------↑
1*silnia(0)
1*1=1
wynik powstaje przy wycofywaniu z rekurencji
---------↑
1

1
(Krótka uwaga - wyrażenia arytmetyczne są tłumaczone na postać beznawiasową; podczas tłumaczenia i obliczania
wartości wyrażeń też jest wykorzystywany stos.
4.
Ilustracja obliczania wartości funkcji rekurencyjnej silnia ze stosem. W rekordzie wywołania poza polami na
parametr i adres powrotu jest miejsce na wynik funkcji.
Wywołanie
2
2
silnia(2)
a4
a4
?
2
Wywołanie
1
1
silnia(1)
a3
a3
?
1
Wywołanie
0
0
silnia(0)
a3 a3
?
1
Złożoność czasowa T(n) i pamięciowa S(n) algorytmu rekurencyjnego silnia(n).
Dla T(n) liczymy mnożenia (operacje dominujące) oraz stały czas r potrzebny na sprawdzenie:
• czy jest wolne miejsce na stosie,
• zarezerwowanie miejsca na nowy rekord wywołania,
• usunięcie rekordu wywołania przy wycofywaniu się z rekurencji.
Równanie rekurencyjne T(0)=r, T(n)=1+r+T(n-1) dla n>=1
Dla S(n) zakładamy, że podczas obliczania wartości funkcji każdy rekord umieszczany na stosie ma stałą liczbę
komórek c.
Równanie rekurencyjne S(0)=c, S(n)=c+S(n–1) dla n>=1.