wyklad 9 zap i, 2 12 2013


POLITECHNIKA WARSZAWSKA
Instytut Automatyki i Robotyki
ZASADY PROGRAMOWANIA KOMPUTERÓW
ZAP  zima 2013
Język programowania: C/C++
Środowisko programistyczne: Qt
Wykład 9 . Wskazniki, zmienne dynamiczne i tablice
dynamiczne jednowymiarowe
2
Alokacja statyczna
Zmienne statyczne globalne - tworzone są w momencie uruchomienia
programu na skutek zawartych w nim deklaracji zmiennych globalnych i
istnieją aż do zakończenia jego wykonywania (zajmują więc cały czas
pamięć).
Zmienne statyczne lokalne - tworzone są w momencie wywołania funkcji
na skutek zawartych w niej deklaracji zmiennych lokalnych i istnieją aż do
wyjścia z funkcji (zajmują więc przez ten czas pamięć).
Przydział pamięci na zmienne statyczne jest to tzw. alokacja statyczna,
która odbywa się automatycznie (bez udziału programisty) w obszarze
pamięci operacyjnej, zwanym stosem (ang. stack) programu. Stos ma
stały rozmiar (rzędu 1-2 MB) i łatwo może ulec przepełnieniu (stack
overflow), np. w przypadku zbyt dużych tablic (przykładowo tablica
522000 elementów typu int już się nie mieści) lub zle napisanej
rekurencji.
3
Alokacja dynamiczna
Zmienne dynamiczne - tworzone są podczas wykonywania programu za
pomocą odpowiedniej instrukcji (new) i istnieją aż do momentu
skasowania, za pomocą przeznaczonej do tego instrukcji (delete).
Zajmują więc pamięć tylko do momentu skasowania. Potem tę samą
pamięć można w tym samym lub innym programie wykorzystać na inne
zmienne dynamiczne (które trzeba od nowa utworzyć).
Przydział pamięci na zmienne dynamiczne, czyli tzw. alokacja
dynamiczna, odbywa się w trakcie pracy programu na żądanie
programisty (instrukcją new) w obszarze pamięci zwanym stertą (ang.
heap). Na stertę wykorzystywana jest cała dostępna pamięć wirtualna
komputera (pamięć RAM oraz pamięć wymiany na dysku twardym).
Może to oznaczać do 2GB, a nawet 3GB pamięci (przestrzeń adresowa w
systemie 32-bitowym to 2^32, czyli 4GB pamięci, z czego 1- 2GB zajmuje
jądro systemu operacyjnego).
4
Alokacja dynamiczna c.d.
Przydzieloną pamięć dynamiczną należy zwolnić odpowiednimi
instrukcjami (delete), czyli  posprzątać po sobie . Jeśli się tego nie zrobi,
pamięć nie będzie (w ogólnym przypadku) zwolniona aż do zamknięcia
systemu; kolejne uruchomienia takiego programu powodują wówczas
ciągłe zmniejszanie się dostępnej pamięci (tzw. wyciek pamięci).
Po co stosujemy zmienne dynamiczne ?
" umożliwiają tworzenie struktur dynamicznych, o nie znanej z góry
wielkości (listy, drzewa), bez niepotrzebnego rezerwowania na zapas
pamięci statycznej;
" umożliwiają tworzenie bardzo dużych tablic;
" można je w każdej chwili podczas działania programu usunąć z pamięci i
na zwolnionym miejscu utworzyć nową zmienną dynamiczną - w tym
samym lub innym programie. Ułatwiają więc równoległą pracę wielu
programów w systemie operacyjnym.
5
Wskazniki i zmienne wskazywane
Do zmiennych statycznych z reguły odwołujemy się poprzez ich nazwy
(identyfikatory):
int i; // definicja zmiennej i
i = 12; // podstaw 12 pod zmienną o nazwie i
Do zmiennych dynamicznych z reguły odwołujemy się poprzez ich adresy
(wskazniki), używając gwiazdki * stojącej po ich lewej stronie:
int *w; w=new int; // deklaracja wskaznika w i przydział pamięci
*w = 1; // podstaw 1 pod zmienną wskazywaną przez w
Zatem:
w - jest tu adresem, czyli wskaznikiem i musi być typu wskaznikowego;
jest to zmienna wskaznikowa, będąca zwykłą zmienną statyczną (ma swoją
nazwę, podobnie jak inne zmienne statyczne, i miejsce w pamięci zajęte przez cały czas
wykonywania programu).
*w - jest zmienną dynamiczną, wskazywaną przez adres w i może być
dowolnego typu (typ ten podaje się przy deklaracji wskaznika). Dlatego
zmienne dynamiczne nazywamy inaczej zmiennymi wskazywanymi.
6
Wskazniki i zmienne wskazywane c.d.
Nazwy wskazników mogą być zupełnie dowolne, tak jak dowolne mogą
być nazwy wszystkich innych zmiennych:
*a = "co to?"; // pod zmienną wskazywaną przez a podstaw napis "co to?"
*wsk_i = 20; // pod zmienną wskazywaną przez wsk_i podstaw liczbę 20
*adres = 3.8; // pod zmienną wskazywaną przez adres podstaw wartość 3.8
cout << *adres << *a; // wydrukuj zmienną wskazywaną przez adres
// i zmienną wskazywaną przez a
Na zmiennych dynamicznych można
wykonywać te same operacje, co na
zmiennych statycznych, tylko należy się do
nich inaczej odwoływać - poprzez
wskaznik, czyli używając operatora * do
wyłuskania wartości tych zmiennych
7
Tworzenie zmiennych dynamicznych
Adres (czyli wskaznik) zmiennej wskazywanej otrzymujemy w trakcie
działania programu w wyniku wykonania instrukcji
Tworzenie zmiennej dynamicznej
nazwa wskaznika = new typ zmiennej wskazywanej ;
Jak to działa:
Operator new przydziela pamięć na zmienną dynamiczną typu podanego i
adres tej zmiennej podstawia pod wskaznik o podanej nazwie. Jeżeli
alokacja (przydział pamięci) się nie powiedzie, np. pamięci już nie
wystarczy, pod wskaznik wstawi adres pusty (NULL, czyli 0).
wsk_i = new int ;
// przydziel pamięć na zmienną typu int i adres zapamiętaj jako wskaznik wsk_i.
adres = new double ;
// przydziel pamięć na zmienną typu double i adres zapamiętaj jako adres
a = new string;
// przydziel pamięć na zmienną typu string i adres zapamiętaj jako a
8
Usuwanie zmiennych dynamicznych
Adres (czyli wskaznik) zmiennej wskazywanej kasujemy w trakcie
działania programu w wyniku wykonania instrukcji
Usuwanie zmiennej dynamicznej
delete nazwa wskaznika ;
Jak to działa:
Operator delete zwalnia pamięć zarezerwowaną na zmienną dynamiczną
pod adresem o podanej nazwie i kasuje ten wskaznik (potem nie można się
już do niego odwoływać).
delete wsk_i;
delete adres;
" Nie wolno używać delete do zwalniania pamięci nie przydzielonej przez
new (np. określonej przez wskaznik do zmiennej statycznej)
" Nie wolno dwukrotnie zwalniać tego samego obszaru pamięci
" Można zastosować delete do wskaznika pustego (nic się nie stanie)
9
Przykłady tworzenia i usuwania zmiennych dynamicznych
int *w;
// deklaracja wskaznika do zmiennej integer;
w = new int;
// przydzielenie pamięci zmiennej typu int wskazywanej przez w
*w = 20;
// podstawienie liczby 20 pod adres w
cout << "Wartosc wskazywana powiekszona o 5 " << (*w)+5 << endl;
// dodanie liczby 5 do wartości wskazywanej przez w
delete w;
// zwolnienie pamięci pod adresem w i skasowanie tego adresu
w - wskaznik (zmienna statyczna)
w
*w
*w - zmienna dynamiczna (wskazywana przez w),
czyli to, co jest pod adresem w
10
Operator & i nietypowe odwołania
Do zmiennych statycznych możemy się dostać za pomocą adresów,
używając operatora &:
int *ws; // ws jest wskaznikiem do zmiennej typu int
int a = 1; // podstaw 1 pod zmienną o nazwie a
ws = & a ; // podstaw pod ws wskaznik do zmiennej a
// zatem od tej pory zamiast a możemy pisać *ws (tylko po co?)
cout << *ws; // wydrukuj to, co jest pod adresem ws (czyli 1)
a - zmienna statyczna
& a - wskaznik (zmienna statyczna) do zmiennej a
& a a
Uwaga: do wskazników tego typu nie wolno stosować
operacji delete.
11
Wskazniki i zmienne wskazywane - przykłady
Zmienne dynamiczne mogą różnych typów, tak jak wszystkie inne
zmienne. Zmienne dynamiczne mogą być dowolnie rozrzucone w
pamięci komputera:
Wskaznik przechowuje adres początku obszaru pamięci przydzielonego
na zapamiętanie zmiennej dynamicznej.
Deklaracja wskaznika
12
Żeby skorzystać ze zmiennej dynamicznej, musimy najpierw
zadeklarować wskaznik do tej zmiennej, podając typ zmiennej
wskazywanej. Piszemy więc tak:
int *wsk_i;
co należy czytać:
zmienna wsk_i jest wskaznikiem do zmiennej typu int.
int *ad1, *ad2, *ad3; // tak deklarujemy kilka wskazników
int *ad1, ad2, ad3; // bo tu wskaznikiem jest tylko ad1
Deklaracja wskaznika
typ zmiennej wskazywanej *nazwa wskaznika;
int *w;
Zapisy równoważne:
int* w;
w jest wskaznikiem do zmiennej typu int
int * w;
UWAGA: Jeśli wskaznik wskazuje rekord, to wcześniej trzeba ten typ
rekordowy, czyli strukturę, zdefiniować.
13
Przykłady deklarowania i używania wskazników
struct Sam {
string marka;
int rocznik;
double cena;
};
int main( ) {
int *wsk_i, *wsk_tab;
string *a;
Sam *ad_auta;
string *wsk_wek;
Zmienna ad_auta jest wskaznikiem
// tu m. in. operacje new
do zmiennej typu Sam:
Tak nie wolno, bo
*wsk_i = 20;
kropka jest
*wsk_tab =5;
*ad_auta . cena = 35000;
silniejsza od
*a = "co to?";
gwiazdki
(*ad_auta) . cena = 35000;
*wsk_wek = "Pawel";
(*ad_auta) . cena = 35000;
*(wsk_wek+1) = "Maciej";
równoważne
wersja krótsza w zapisie:
// tu muszą być operacje delete
return 0; ad_auta -> cena = 35000;
}
wskaznikowy sposób działania na tablicach (o tym dalej)
Dynamiczna rezerwacja tablic jednowymiarowych 14
int main( ) { // zarezerwujemy tablicę t typu int
int *t ; // t jest wskaznikiem do pierwszego elementu tablicy
int n;
cout << "ile elementow w tablicy ? " << endl;
cin >> n;
t=new int [n]; // rezerwujemy pamięć dla n elementów tablicy typu int
// i adres pierwszego z nich zapamiętujemy jako t
...
for (int i=0; i> t[ i ]; // zwykłe działania na tablicy
...
delete [ ] t; // tak zwalniamy pamięć przydzieloną na tablicę
...
Zamiast :
...
Tak nie wolno działać na
return 0;
int *t ;
tablicach statycznych:
}
t=new int [n];
int n;
cin >> n; można napisać od razu:
int t [n] ;
int *t = new int [n];
15
Tablice w notacji wskaznikowej
Wszystkie elementy tablicy są umieszczone w ciągłym obszarze
pamięci jeden za drugim:
Nazwa tablicy użyta w wyrażeniach jest adresem początkowego elementu
tablicy (uwaga: wyjątkiem jest funkcja sizeof, która odnosi się do całej tablicy)
wskazniki
zmienna przechowująca
adres
Do elementów tablic rezerwowanych dynamicznie możemy się odwoływać
w sposób tradycyjny, zgodnie z notacją tablicową.
Ale notacja wskaznikowa z natury rzeczy umożliwia szybszy, bezpośredni
dostęp do elementów tablicy, nie wymagający wyznaczania wartości
indeksów, a tylko dodawania liczb naturalnych do adresu.


Wyszukiwarka

Podobne podstrony:
wyklad zap i, 12 2013
wyklad zap i, 12 2013
wyklad 7 zap i, 11 2013
wyklad 8 zap i, 11 2013
wyklad 3 zap i,! 10 2013
wyklad 5 zap i, 4 11 2013
wyklad 1 zap i, 7 10 2013
wyklad 2 zap i, 10 2013
wyklad 6 zap i, 11 2013
wyklad 4 zap i,( 10 2013 poprawiony
wyklad 12 2013
KPC Wykład (19) 12 03 2013
KPC Wykład (15) 12 02 2013
Wykład 2 10 3 12
Wyklad 1 CIAGI 12 wer stud
Wyklad ElementyProg 12 08
Sem 4 Wykład nr 9 Interakcje 2013

więcej podobnych podstron