Czym jest stos i sterta?

Podobne dokumenty
Obiekt klasy jest definiowany poprzez jej składniki. Składnikami są różne zmienne oraz funkcje. Składniki opisują rzeczywisty stan obiektu.

Wskaźnik może wskazywać na jakąś zmienną, strukturę, tablicę a nawet funkcję. Oto podstawowe operatory niezbędne do operowania wskaźnikami:

Diagram klas UML jest statycznym diagramem, przedstawiającym strukturę aplikacji bądź systemu w paradygmacie programowania obiektowego.

Informacje ogólne. Karol Trybulec p-programowanie.pl 1. 2 // cialo klasy. class osoba { string imie; string nazwisko; int wiek; int wzrost;

Rozdział 4 KLASY, OBIEKTY, METODY

Programowanie Komputerów

Języki i techniki programowania Ćwiczenia 2

Java - tablice, konstruktory, dziedziczenie i hermetyzacja

Obszar statyczny dane dostępne w dowolnym momencie podczas pracy programu (wprowadzone słowem kluczowym static),

Podczas dziedziczenia obiekt klasy pochodnej może być wskazywany przez wskaźnik typu klasy bazowej.

Wykład 4: Klasy i Metody

Programowanie obiektowe Wykład 3. Dariusz Wardowski. dr Dariusz Wardowski, Katedra Analizy Nieliniowej, WMiI UŁ 1/21

Klasy. dr Anna Łazińska, WMiI UŁ Podstawy języka Java 1 / 13

Aplikacje w środowisku Java

Lab 9 Podstawy Programowania

Wskaźniki a tablice Wskaźniki i tablice są ze sobą w języku C++ ściśle związane. Aby się o tym przekonać wykonajmy cwiczenie.

PROE wykład 2 operacje na wskaźnikach. dr inż. Jacek Naruniec

Podstawy programowania obiektowego

Java. język programowania obiektowego. Programowanie w językach wysokiego poziomu. mgr inż. Anna Wawszczak

IMIĘ i NAZWISKO: Pytania i (przykładowe) Odpowiedzi

Deklaracja struktury w C++

Czym są właściwości. Poprawne projektowanie klas

Techniki programowania INP001002Wl rok akademicki 2018/19 semestr letni. Wykład 3. Karol Tarnowski A-1 p.

Klasa jest nowym typem danych zdefiniowanym przez użytkownika. Najprostsza klasa jest po prostu strukturą, np

Wykład 5 Okna MDI i SDI, dziedziczenie

Informatyka I. Klasy i obiekty. Podstawy programowania obiektowego. dr inż. Andrzej Czerepicki. Politechnika Warszawska Wydział Transportu 2018

Klasy Obiekty Dziedziczenie i zaawansowane cechy Objective-C

Wykład 5: Klasy cz. 3

MATERIAŁY DO ZAJĘĆ II

Konwersje napis <-> liczba Struktury, unie Scanf / printf Wskaźniki

Dariusz Brzeziński. Politechnika Poznańska, Instytut Informatyki

W dowolnym momencie można zmienić typ wskaźnika.

C++ - przeciążanie operatorów. C++ - przeciążanie operatorów. C++ - przeciążanie operatorów. C++ - przeciążanie operatorów

Wstęp do programowania INP001213Wcl rok akademicki 2017/18 semestr zimowy. Wykład 6. Karol Tarnowski A-1 p.

Polimorfizm, metody wirtualne i klasy abstrakcyjne

Szablony klas, zastosowanie szablonów w programach

TEMAT : KLASY DZIEDZICZENIE

JAVA W SUPER EXPRESOWEJ PIGUŁCE

Kurs programowania. Wykład 1. Wojciech Macyna. 3 marca 2016

Języki i metodyka programowania. Język C# pętle, sterowanie, wyjątki

Wprowadzenie w dziedziczenie. Klasa D dziedziczy klasę B: Klasa B klasa bazowa (base class), klasa D klasa pochodna (derived class).

Dokumentacja do API Javy.

Programowanie obiektowe i zdarzeniowe

1 Wskaźniki i zmienne dynamiczne, instrukcja przed zajęciami

Konstruktory. Streszczenie Celem wykładu jest zaprezentowanie konstruktorów w Javie, syntaktyki oraz zalet ich stosowania. Czas wykładu 45 minut.

Dziedziczenie. Streszczenie Celem wykładu jest omówienie tematyki dziedziczenia klas. Czas wykładu 45 minut.

1. Które składowe klasa posiada zawsze, niezależnie od tego czy je zdefiniujemy, czy nie?

1. Wartość, jaką odczytuje się z obszaru przydzielonego obiektowi to: a) I - wartość b) definicja obiektu c) typ oboektu d) p - wartość

Typy wyliczeniowe Konwersje napis <-> liczba Struktury, unie Scanf / printf Wskaźniki

zmienne stanowią abstrakcję komórek pamięci: programista może przechowywać dane w pamięci, nie martwiąc się o techniczne szczegóły (np.

Podstawy programowania. Wykład Funkcje. Krzysztof Banaś Podstawy programowania 1

KLASA UCZEN Uczen imię, nazwisko, średnia konstruktor konstruktor Ustaw Wyswietl Lepszy Promowany

Programowanie obiektowe

Wskaźniki i dynamiczna alokacja pamięci. Spotkanie 4. Wskaźniki. Dynamiczna alokacja pamięci. Przykłady

Interfejsy. Programowanie obiektowe. Paweł Rogaliński Instytut Informatyki, Automatyki i Robotyki Politechniki Wrocławskiej

Strona główna. Strona tytułowa. Programowanie. Spis treści. Sobera Jolanta Strona 1 z 26. Powrót. Full Screen. Zamknij.

Definicje klas i obiektów. Tomasz Borzyszkowski

Kurs WWW. Paweł Rajba.

Java - wprowadzenie. Programowanie Obiektowe Mateusz Cicheński

Temat: Dynamiczne przydzielanie i zwalnianie pamięci. Struktura listy operacje wstawiania, wyszukiwania oraz usuwania danych.

Język C++ Różnice między C a C++

Co to jest klasa? Z programistycznego punktu widzenia klasa stanowi typ danych, który odwzorowuje wspólne cechy jakiegoś obiektu.

Laboratorium 03: Podstawowe konstrukcje w języku Java [2h]

Zaawansowane programowanie w języku C++ Klasy w C++

Podstawy programowania w języku C++

C++ - klasy. C++ - klasy. C++ - klasy. C++ - klasy. C++ - klasy KONSTRUKTORY

KLASA UCZEN Uczen imię, nazwisko, średnia konstruktor konstruktor Ustaw Wyswietl Lepszy Promowany

Program 6. Program wykorzystujący strukturę osoba o polach: imię, nazwisko, wiek. W programie wykorzystane są dwie funkcje:

> C++ dynamiczna alokacja/rezerwacja/przydział pamięci. Dane: Iwona Polak. Uniwersytet Śląski Instytut Informatyki

JĘZYKI PROGRAMOWANIA Z PROGRAMOWANIEM OBIEKTOWYM. Wykład 6

Java: kilka brakujących szczegółów i uniwersalna nadklasa Object

PROE wykład 3 klasa string, przeciążanie funkcji, operatory. dr inż. Jacek Naruniec

PARADYGMATY PROGRAMOWANIA Wykład 4

Wykład 2: Podstawy Języka

Programowanie obiektowe. Materiały przygotował: mgr inż. Wojciech Frohmberg

Jak napisać listę jednokierunkową?

DYNAMICZNE PRZYDZIELANIE PAMIECI

C++ - klasy. C++ - klasy. C++ - klasy. C++ - klasy. C++ - klasy INNE SPOSOBY INICJALIZACJI SKŁADOWYCH OBIEKTU

Wykład 8: klasy cz. 4

Wstęp do programowania obiektowego. WYKŁAD 3 Dziedziczenie Pola i funkcje statyczne Funkcje zaprzyjaźnione, this

Programowanie w środowiskach graficznych. Wykład 3 Język C#

Spis treści. 1 Java T M

2. Tablice. Tablice jednowymiarowe - wektory. Algorytmy i Struktury Danych

Wskaźniki. Informatyka

Ok. Rozbijmy to na czynniki pierwsze, pomijając fragmenty, które już znamy:

Programowanie obiektowe

JAVA. Java jest wszechstronnym językiem programowania, zorientowanym. apletów oraz samodzielnych aplikacji.

Programowanie obiektowe. Literatura: Autor: dr inŝ. Zofia Kruczkiewicz

Podstawy programowania. Wykład 6 Wskaźniki. Krzysztof Banaś Podstawy programowania 1

C++ - [4-7] Polimorfizm

Java Język programowania

C++ - klasy. C++ - klasy. C++ - klasy. C++ - klasy. C++ - klasy PRAWA PRZYJACIÓŁ KLASY. Dostęp z zewnątrz: Dostęp z wewnątrz:

znajdowały się różne instrukcje) to tak naprawdę definicja funkcji main.

Wprowadzenie do języka Java

Podstawy programowania komputerów

Programowanie obiektowe i zdarzeniowe wykład 4 Kompozycja, kolekcje, wiązanie danych

Biuro Podróży 0. Stwórz projekt aplikacja konsolowa lub WPF (przemyśl wybór, bo zmiana może być czasochłonna). 1. Stwórz abstrakcyjną klasę

Dziedziczenie. dr Jarosław Skaruz

Jeśli chcesz łatwo i szybko opanować podstawy C++, sięgnij po tę książkę.

> C++ wskaźniki. Dane: Iwona Polak. Uniwersytet Śląski Instytut Informatyki 26 kwietnia 2017

Transkrypt:

W języku C# istnieje kilka podstawowych typów danych. Na pierwszy rzut oka nie widać między nimi żadnej różnicy, jednak pojawia się w charakterystycznych sytuacjach takich jak przekazywanie parametrów do funkcji czy kopiowanie wartości zmiennych. Dokładne zapoznanie się z typami danych pozwoli Ci unikać błędów charakterystycznych dla początkujących programistów. Czym jest stos i sterta? Poruszając temat typów danych warto wspomnieć kilka słów o stosie i stercie. Po pierwsze nie należy tych terminów mylić ze strukturami do przechowywania danych, możliwych do implementacji w wielu językach. Zarówno stos jak i sterta są częścią pamięci wirtualnej jaka jest przydzielona aplikacji podczas uruchamiania są odrębne dla każdej aplikacji. Każdy utworzony wątek danej aplikacji korzysta z osobnego stosu, więc jedna aplikacja może mieć ich kilka. Stos jest o wiele szybszy od sterty, lądują na niego wszelkie zmienne oraz parametry przekazywane do funkcji. Na stos zostaje wrzucany także adres powrotu. Z programistycznego punktu widzenia stos nas nie obchodzi. Porządek na stosie w pewnym sensie utrzymuje system. Deklarując zmienną trafia ona na stos i zostaje z niego zdjęta (usunięta), wtedy gdy wypadnie poza klamry zasięgu (blok kodu ). Generalnie programista nie musi o nic dbać, ma dbać tylko o poprawny, czysty kod. Sterta jest miejscem w pamięci wirtualnej procesu, gdzie trafiają wszelkie klasy i ich instancje. Szczególnie rozpatrując język C# na stertę trafiają także interfejsy, tablice, delegaty. Sterta jest zarządzana nie przez system operacyjny, a przez wirtualną maszynę.net. Elementy trafiające na stertę są tworzone operatorem new (nie jest to żelazną zasadą) którego zasada działania jest bardziej skomplikowana niż zwykła deklaracja zmiennych lądujących na stos. W innych językach programowania (C++) o porządek na stercie musimy zadbać sami zwalniając pamięć. W C# pamięć sterty kontroluje Garbage Collector, wyłapuje puste referencje i zwalnia miejsce. Ciężko mi na temat sterty/stosu napisać coś więcej, ponieważ nie można ich porównywać bezpośrednio między dwoma różnymi językami i nie chcę popełnić gafy. W C++ programista wszystko robi sam, dlatego ma świadomość tego co robi. C# jest językiem bardzo Karol Trybulec p-programowanie.pl 1

zautomatyzowanym i wygodnym, lecz wiele rzeczy dzieje się za naszymi plecami i bez naszej wiedzy. Typy wartościowe Podstawowym typem danych występującym w języku C# jest typ wartościowy. Jest to typ występujący powszechnie we wszystkich językach programowania. Typ wartościowy jest podstawowym typem danych występującym w C#. Podczas deklaracji typu wartościowego kompilator alokuje odpowiednią ilość miejsca w pamięci, w której będzie przechowywana wartość zmiennej. Typy wartościowe zawsze umieszczane są na stosie. Wszystkie typy wartościowe dziedziczą niejawnie z klasy System.ValueType. To właśnie ta klasa zapewnia nas, że obiekt zostanie umieszczony stosie. Dzięki temu program ma do nich bardzo szybki dostęp. Typy wartościowe są domyślnie przekazywane przez wartość, oznacza to, że do funkcji przekazywana jest ich kopia. Typami wartościowymi w C# są: int, double, float, long, byte, char, bool, typy wyliczeniowe enum, struktury. Przykładowo, deklarując zmienną int deklarujemy typ wartościowy o wielkości 4b. Klasą pochodną jest klasa System.ValueType a więc zmienna będzie znajdować się na stosie. Mamy pewność, że znajduje się ona poza działaniem Garbage Collectora. Ponieważ System.ValueType jest klasą bazową wszystkich typów wartościowych poprawny będzie poniższy zapis: 1 ValueType a = true; 2 ValueType b = 5; 4 Console.WriteLine(a.GetType()); //System.Boolean 5 Console.WriteLine(b.GetType()); //System.Int2 7 int c = (int)b; // wszystko ok Kopiowanie wartości typów wartościowych jest takie samo jak we wszystkich innych językach. Kopię osiągamy poprzez operator przypisania =. Przekazując parametr do funkcji operujemy na jego kopii, więc po wyjściu z bloku funkcji oryginalna zmienna nie zostaje zmieniona. Karol Trybulec p-programowanie.pl 2

Podsumowanie typu wartościowego: wszystkie niejawnie rozszerzają klasę System.ValueType dzięki temu wszystkie trafiają na stos zmienne typów wartościowych są po prostu miejscami w pamięci są domyślnie przekazywane przez wartość (kopia) zmienna przestaje istnieć kiedy wyjdzie poza klamry zasięgu mogą mięć konstruktory niestandardowe nie są w żadnym wypadku zależne od Garbage Collectora Typy referencyjne Drugim rodzajem typu danych są typy referencyjne. Występują one także w języku C++. Na omówieniu tego typu danych skupimy się bardziej szczegółowo, ponieważ istnieją w nim charakterystyczne mechanizmy, które należy zapamiętać. Typ referencyjny jest typem umieszczanym na stercie programu. Konkretniej referencja do pamięci umieszczana jest na stosie a obszar pamięci do jakiego prowadzi referencja znajduje się na stercie. Typami referencyjnymi w C# są elementy rozszerzające klasę System.Object oraz System.String, a więc są to np.: klasy, delegacje, interfejsy, tablice, zmienne string itd. Typów referencyjnych nie da się kopiować korzystając z operatora przypisania. Główną różnicą między typami wartościowymi a referencyjnymi jest niezmienność wielkości typów wartościowych w przeciwieństwie do typów referencyjnych. Nigdy nie wiemy ile miejsca w pamięci będzie zajmować klasa, w przeciwieństwie do prostych zmiennych np. typu int, które zajmują 4b. Dlatego też, tworząc zmienną liczbową kompilator przydziela jej po prostu odpowiednią ilość miejsca. Tworząc klasę (typ referencyjny), kompilator wrzuca na stos referencję, która wskazuje na obszar pamięci znajdujący się na stercie. Obiekt klasy może się zmieniać w trakcie działania programu. Nad wszystkim czuwa Garbage Collector działający w osobnym wątku naszej aplikacji (sterta jest wspólna dla wszystkich wątków). Podsumowanie typu referencyjnego wszystkie niejawnie dziedziczą z klasy System.Object lub System.String. dzięki temu wszystkie trafiają na stertę (ale referencja do obiektu na stos) są domyślnie przekazywane do funkcji przez wartość Karol Trybulec p-programowanie.pl

są usuwane z pamięci za pomocą Garbage Collectora mogę mieć konstruktory Kopiowanie wartości typów referencyjnych Jaki jest problem? Typów referencyjnych nie można kopiować zwykłym operatorem przypisania. Dlaczego? Rozważmy dwa poniższe przykłady. W pierwszej kolejności prosty przykład z typem wartościowym: 1 2 4 5 7 8 9 10 int wiek1 = 20; int wiek2 = wiek1; Console.WriteLine(wiek1); // wyswietli 20 Console.WriteLine(wiek2); // wyswietli 20 wiek2 = 50; Console.WriteLine(wiek1); // wyswietli 20 Console.WriteLine(wiek2); // wyswietli 50 W tym kodzie chyba nic nas nie dziwi. W 2 linijce następuje skopiowane wartości zmiennej wiek1 do zmiennej wiek2. Obie zmienne ciągle znajdują się w osobnych miejscach w pamięci, więc gdy w linijce 7 zmieniamy wartość zmiennej wiek2 to zmienna wiek1 się nie zmienia. Teraz ten sam przykład, z wykorzystaniem typów referencyjnych: 1 2 4 5 7 8 9 10 11 12 1 14 15 1 17 18 19 20 21 22 2 24 25 2 class Osoba public String imie get; set; public Osoba(string imie) this.imie = imie; public void Info() Console.WriteLine("imie: 0", imie); class Program static void Main(string[] args) Osoba osoba1 = new Osoba("Karol"); Osoba osoba2 = osoba1; Console.WriteLine(osoba1.imie); // wyswietli Karol Console.WriteLine(osoba1.imie); // wyswietli Karol osoba1.imie = "Karol"; osoba2.imie = "Arek"; Console.WriteLine(osoba1.imie); // wyswietli Arek Console.WriteLine(osoba1.imie); // wyswietli Arek Console.ReadKey(); Karol Trybulec p-programowanie.pl 4

Mamy niezgodność. W linijce 1 skopiowaliśmy imię osoba1 do osoba2, więc dwa razy zobaczyliśmy napis Karol. Jednak w linijce 18 i 19 ustawiliśmy dwa osobne imiona. Mimo tego dwa razy zostało wyświetlone imię Arek. Co jest nie tak? Operator przypisania kopiuje wartość zmiennej z prawej strony do zmiennej z lewej strony. Ponieważ klasy są typami referencyjnymi dlatego zmienne osoba1 i osoba2 także są referencjami. Referencja jest to zmienna, której wartością jest adres do miejsca w pamięci, gdzie przechowywana jest jakaś wartość lub instancja obiektu. Skoro wartością referencji jest adres, to operator przypisania skopiował właśnie adres na jaki referencja wskazuje. Nie należy mylić adresu referencji w pamięci, z adresem na jaki wskazuje referencja (czyli wartość referencji). Powyższy obrazek ukazuje sedno problemu. Teraz dokładnie widać, że dwie referencje na stosie wskazują na to samo miejsce (na tę samą instancję klasy osoba) na stercie. Jest to ewidentny błąd. Aby skopiować wartości pól poszczególnych obiektów, trzeba to robić pojedynczo pole po polu. Można też kopiować całe obiekty. Są z tym związane pojęcia kopii płytkiej oraz kopii głębokiej, jednak jest to materiał na osobny artykuł. Przekazywanie typów referencyjnych do funkcji przez wartość Istnieje prosta zasada jaką trzeba zapamiętać, przesyłając typy referencyjne do funkcji poprzez wartość. Wewnątrz funkcji możemy zmienić stan obiektu na jaki wskazuje przesłana referencja, jednak nie można tej referencji zmienić. Przeanalizujmy poniższy program: Karol Trybulec p-programowanie.pl 5

1 2 4 5 7 8 9 10 11 12 1 14 15 1 17 18 19 20 21 22 2 24 25 2 27 class Osoba public String imie get; set; public Osoba(string imie) this.imie = imie; public void Info() Console.WriteLine("imie: 0", imie); class Program static void Main(string[] args) Osoba osoba1 = new Osoba("Karol"); osoba1.info(); // imie: Karol Zmien(osoba1); osoba1.info(); //imie: Arek Console.ReadKey(); public static void Zmien(Osoba o) o.imie = "Arek"; // zadziała o = new Osoba("Maciek"); // nie zadziała Tworzymy instancję klasy Osoba i wypełniamy ją danymi. Przekazujemy referencję do obiektu do funkcji poprzez wartość. Do funkcji powędrowała kopia referencji, ponieważ jest to charakterystyczne dla przekazywania przez wartość. Mimo, że przesłaliśmy kopię referencji, jej wartość wskazuje na stercie na oryginalny obszar pamięci zawierający instancję klasy Osoba. Dlatego przypisanie nowego imienia w linijce 24 zadziała. Mimo że przesłaliśmy kopię referencji, operujemy na oryginalnym bloku pamięci na stercie. W przypadku gdybyśmy przesyłali typ wartościowy, sytuacja wyglądała by całkiem inaczej! Mielibyśmy wewnątrz funkcji kopię wartości, a więc jakiekolwiek operacje wewnątrz funkcji nie zmieniały by obiektu z poza niej. W linijce 25 nie jesteśmy w stanie zmienić wartości referencji, ponieważ tak samo jak w przypadku przesyłania typów wartościowych, zmiany nie będą widoczne poza ciałem funkcji. Karol Trybulec p-programowanie.pl

Przekazywanie typów referencyjnych do funkcji przez referencję Łatwo przewidzieć efekt jaki uzyskamy, przesyłając argumenty typu referencyjnego poprzez referencję. Osiągniemy pełną możliwość modyfikacji instancji obiektu jak i wartości przekazanej referencji. Zmodyfikujmy poprzedni kod dodając ref przed nazwą argumentu: 1 2 4 5 7 8 9 10 11 12 1 14 15 1 17 18 19 20 21 22 2 24 25 2 27 class Osoba public String imie get; set; public Osoba(string imie) this.imie = imie; public void Info() Console.WriteLine("imie: 0", imie); class Program static void Main(string[] args) Osoba osoba1 = new Osoba("Karol"); osoba1.info(); // imie: Karol Zmien(ref osoba1); osoba1.info(); //imie: Maciek Console.ReadKey(); public static void Zmien(ref Osoba o) o.imie = "Arek"; // zadziała o = new Osoba("Maciek"); // także dzaiała! Tym razem w linijce 25 nadpisujemy referencję nową instancją klasy Osoba. Uzyskujemy pełną kontrolę zarówno nad wartością referencji jak i instancją klasy, na którą wskazuje. Po drodze w linii 25 gubimy referencję do jednej z instancji. Zostanie to wykryte przez Garbage Collector a obiekt zostanie usunięty ze sterty. Podsumowanie Omówione przykłady rozjaśniają nieco koncepcję zarządzania pamięcią w języku C#. Karol Trybulec p-programowanie.pl 7

Wnioski są następujące: Przekazywanie typów referencyjnych do funkcji Jeżeli typ referencyjny jest przekazywany przez wartość, możemy zmieniać tylko stan obiektu na który wskazuje Jeżeli typ referencyjny jest przekazywany przez referencję, możemy zmieniać stan obiektu na który wskazuje, oraz wartość referencji. W kolejnych wpisach poruszę kwestię kopii płytkiej i głębokiej oraz konwersji boxing/unboxing. Karol Trybulec p-programowanie.pl 8