wykład 1 Agata Półrola Wydział Matematyki i Informatyki UŁ semestr zimowy 2018/2019
strona www: http://www.math.uni.lodz.pl/~polrola podstrona przedmiotu: Materiały dla studentów
Najpierw trochę o komputerach... Komputer - urządzenie przechowujące i przetwarzające informacje Działanie komputera jest kontrolowane przez wykonywany przez niego program
Komputer - podstawowe elementy procesor - mózg komputera. Zawiera m.in.: układ sterujący (CU - control unit) - element nadzorujący i koordynujący działanie komputera jednostkę wykonawczą (EU - execution unit), a w niej m.in. jednostkę arytmetyczną (ALU - arithmetic-logic unit) - element odpowiedzialny za operacje arytmetyczne i logiczne różnego rodzaju rejestry (wbudowane w procesor komórki pamięci o niewielkim pamięć operacyjna rozmiarze i szybkim dostępie, służące do przechowywania wyników obliczeń, adresów w pamięci operacyjnej, instrukcji; procesor wykonuje działania korzystając ze swoich rejestrów - dane są pobierane z pamięci do rejestrów a potem odsyłane). jednostki wejścia/wyjścia pamięć pomocnicza (zewnętrzna)
Pamięć Pamięć komputera może być traktowana jako ciąg komórek pamięci (nazywanych czasami słowami). Każda komórka ma pewien adres, określający jej pozycję w pamięci. Każda komorka składa się z pewnej liczby bitów (zazwyczaj 8, 16, 32 lub 64). 8 bitów tworzy bajt Każdy bit przechowuje cyfrę dwójkową (0 lub 1) za pomocą ciągów cyfr dwójkowych zapisujemy liczby całkowite, rzeczywiste, wartości logiczne, instrukcje programu itd
...trochę o programach... Program - zapisany w sposób zrozumiały dla komputera ciąg poleceń, które komputer ma wykonać aby zrealizować określone zadanie
Wykonanie programu Wykonywany program zajmuje grupę powiązanych ze sobą komórek pamięci. Komórki lub grupy komórek zawierają poszczególne instrukcje programu. program to ciąg instrukcji podczas wykonania programu CU czyta kolejne instrukcje z pamięci i zapewnia, że są one wykonywane w odpowiednim porządku instrukcja mówi komputerowi, że powinien wykonać jakieś zadanie (np. przenieść zawartość komórki pamięci znajdującej się pod jakimś adresem do określonego rejestru procesora, albo dodać dwie liczby w ALU)
Postać programu ( wewnątrz komputera ) Instrukcja jest zapisana jako pewien ciąg zer i jedynek. instrukcja o tej samej roli może mieć różną postać w rożnych modelach komputerów Program zapisany w takiej postaci może być wykonany przez komputer. Mówimy, że jest w postaci kodu maszynowego instrukcje kodu maszynowego nazywa się też rozkazami maszynowymi Przykład 0011001111110011 0011111110000110 1001001111001111
... i o językach programowania Przykład LOAD A ADD B STORE C początkowo programy były pisane w kodzie maszynowym kolejnym etapem był język asembler język używający krótkich nazw odpowiadających poszczególnym rozkazom maszynowym danego typu procesora oraz pozwalający na użycie nazw zmiennych zamiast ich adresów przetłumaczenie takiego programu na kod maszynowy jest bardzo proste język asembler jest w użyciu; pozwala pisać bardzo wydajne programy
Przykład kolejnym etapem są języki programowania wysokiego poziomu (np. Pascal, Ada, C++, Java,...) prosty zapis operacji C := A + B programy napisane w takich językach są tłumaczone na kod maszynowy przy pomocy specjalnego translatora
Języki kompilowane Program napisany w języku programowania ma postać pliku tekstowego. Ta postać nazywana jest kodem źródłowym Aby program można było wykonać, musi zostać przetłumaczony na kod wykonywalny (program wykonywalny) zapisany w języku maszynowym proces tłumaczenia programu nazywany jest kompilacją
proces tłumaczenia programu składa się tak naprawdę z dwóch czynności: kompilacji i łączenia kompilacja (przetworzenie kodu źródłowego przez program zwany kompilatorem) zamienia program źródłowy na plik binarny zawierający program w kodzie maszynowym (object file). Najczęściej nie jest on jeszcze końcową wersją programu - zawiera odwołania do zewnętrznych podprogramów umieszczonych w bibliotekach kompilacja może być poprzedzona analizą kodu źródłowego wykonywaną przez program zwany preprocesorem (często kompilator spełnia równocześnie rolę preprocesora) łączenie (konsolidacja, linkowanie), wykonywane przez program zwany konsolidatorem, tworzy kolejny plik binarny, w którym jest już wszystko co jest potrzebne do wykonania programu na danym sprzęcie z danym systemem operacyjnym
Języki interpretowane (skryptowe) Programy napisane w niektórych językach programowania (np. w języku Perl lub Python) uruchamiane są w inny sposób. do uruchomienia programu potrzebne jest środowisko uruchomieniowe - interpreter interpreter nie tworzy programu wykonywalnego; poszczególne instrukcje programu tłumaczone są po kolei na bieżąco na kod maszynowy i wykonywane. Odwołania do podprogramów umieszczonych w zewnętrznych bibliotekach również przetwarzane są na bieżąco
Etapy tworzenia programu Aby napisać program musimy: 1 określić wymagania programu (faza analizy wymagań i specyfikacji) 2 zaprojektować sposób rozwiązania problemu (faza projektowa) 3 zapisać w/w projekt w wybranym języku programowania (faza implementacji) 4 skompilować i uruchomić program 5 sprawdzić, czy program działa poprawnie (faza testowania)... z czego faza implementacji jest chyba najprostsza... ;-)
Algorytmy Aby napisać program, musimy znaleźć odpowiedni algorytm - czyli opisać, jak rozwiązać konkretny problem / zadanie Algorytmy można zapisywać na różne sposoby: w języku naturalnym w postaci schematu blokowego w postaci instrukcji programu w pseudokodzie...
Przykłady algorytmów przepis na ciasto instrukcja składania mebli przepis na rozwiązanie równania kwadratowego postaci ax 2 + bx + c = 0 metoda znalezienia największej liczby w (bardzo, bardzo długim) ciągu liczb
Elementy algorytmów Sposób opisu algorytmu musi pozwalać na wyrażenie: sekwencyjności wyboru iteracji rekursji (rekurencji)
Sekwencyjność Opisujemy kolejne kroki. Kroki muszą być wykonywane w takiej kolejności, w jakiej zostały zapisane Przykład weź 4 jajka, szklankę cukru i cukier waniliowy utrzyj żółtka z cukrem przygotuj szklankę mąki z łyżeczką proszku do pieczenia dodaj część mąki do ucieranej masy ubij pianę z białek dodaj do ucieranej masy pianę i resztę mąki...
Wybór Opisujemy fakt konieczności podjęcia decyzji którą z alternatywnych ścieżek wybrać Przykłady jeżeli ucierana masa jest zbyt gęsta, to dodaj łyżkę wody jeżeli uczestnik turnieju odpowiedział poprawnie na co najmniej 5 pytań, to przechodzi do następnego etapu, w przeciwnym razie odpada z gry jeżeli wyróżnik trójmianu kwadratowego jest ujemny, to piszemy że trójmian nie ma pierwiastków, jeżeli wyróżnik jest równy zero, to piszemy że trójmian ma jeden pierwiastek, w pozostałych przypadkach trójmian ma dwa pierwiastki
Iteracja Opisujemy fakt, że pewna część algorytmu będzie powtarzana - albo określoną ilość razy, albo do momentu zajścia pewnego warunku Przykłady podgrzewaj masę mieszając, dopóki cukier się nie rozpuści powtórz 100 razy czynność idź krok na północ (czyli idź 100 kroków na północ)
Rekurencja Problem dzielimy na mniejsze (zagnieżdżone) podproblemy o podobnej strukturze. Każdy z nich rozwiązujemy stosując ten sam algorytm. Przykład aby obliczyć 5!, oblicz 4! i pomnóż przez 5 aby obliczyć 4!, oblicz 3! i pomnóż przez 4 aby obliczyć 3!, oblicz 2! i pomnóż przez 3... 0!=1
Aby tworzyć algorytmy, powinniśmy rozumieć co jest w stanie zrobić komputer W przypadku bardziej złożonego problemu wygodnie jest: podzielić problem na mniejsze, prostsze podproblemy zaprojektować rozwiązanie dla każdego z nich, w razie potrzeby znów dzieląc je na podproblemy Jest to tzw. projektowanie od ogółu do szczegółu albo z góry w dół (top-down design) Kolejny krok to zapisanie algorytmu w wybranym języku programowania
Każdy język ma swoją składnię: słowa kluczowe instrukcje...
PROGRAMOWANIE JEST JEDNO tylko językow programowania wiele
Język C++ Na wykładzie / ćwiczeniach będziemy używać języka C++ jeden z bardziej popularnych języków programowania powstały w latach 80-tych XX wieku jako rozszerzenie języka C o mechanizmy programowania obiektowego kolejne standardy: C++98, C++11, C++14, C++17, C++20... gdzie szukać opisów języka: http://cppreference.com (polski fragment: http://pl.cppreference.com) http://www.cplusplus.com...
Co jest potrzebne Musimy zaopatrzyć się w jakiś kompilator C++ (albo w odpowiednie IDE, tj. zintegrowane środowisko programistyczne - Integrated Development Environment - dla danego języka) Przykłady IDE: Code::Blocks Dev-C++ Eclipse VisualStudio CLion CodeLite...
Przykładowy program # include < iostream > /* dyrektywa preprocesora */ int main () // program glowny { std :: cout << " Dzien dobry! \n" ; std :: cout << " To ja, Twoj pierwszy program "; } return 0;
Dyrektywy preprocesora # include < iostream > linia zaczyna się od dyrektywy preprocesora preprocesor analizuje kod źródłowy poszukując przeznaczonych dla niego poleceń (dyrektyw lub makr) celem wykonania preprocesora jest przekształcenie kodu programu zgodnie z treścią tych poleceń w przypadku dyrektywy #include przekształcenie polega na dołączeniu w danym miejscu kodu pliku podanego po dyrektywie tu dołączamy plik (bibliotekę) iostream ponieważ będziemy używać tzw. strumieni do wypisania tekstu uwaga - preprocesor nie dokonuje kontroli poprawności programu!
Komentarze # include < iostream > /* dyrektywa preprocesora */ int main () // program glowny Komentarz to taki fragment zawartości pliku z kodem źródłowym programu, który nie jest brany pod uwagę przy kompilacji. W C++ istnieją dwa sposoby wstawiania komentarzy: komentarzem jest tekst zawarty między /* (otwarciem komentarza) i */ (zamknięciem komentarza) komentarzy tego typu nie można zagnieżdżać! komentarzem jest też tekst rozpoczynający się od // jest traktowany jako komentarz; końcem komentarza jest wówczas koniec linii
Definicja głównej funkcji programu int main () // program glowny {... // tresc programu return 0; } funkcja o nazwie main jest właściwym programem w C++. Nazwa ta jest zastrzeżona (nie można jej zmienić) każda funkcja zwraca wartość pewnego typu; dla main jest to zawsze int - wartość całkowita. Typ wyniku jest podany przed nazwą funkcji. Na końcu treści programu znajduje się instrukcja return 0; powodująca zwrócenie zera (oznaczającego zazwyczaj poprawne wykonanie programu) funkcja może przyjmować również pewne dane wejściowe - argumenty. Listę argumentów podaje się w nawiasach () następujących po nazwie funkcji. Pusta lista argumentów oznaczana jest pustymi nawiasami nawiasy klamrowe { i } wyznaczają początek i koniec bloku zawierającego instrukcje programu każda instrukcja programu jest zakończona średnikiem
Strumień wyjścia std::cout std :: cout << " Dzien dobry! \n" ; std :: cout << " To ja, Twoj pierwszy program "; użycie tzw. strumienia std::cout pozwala na przesłanie danych na standardowe wyjście (ekran) znaki << (nazywane operatorem strumienia wyjścia pozwalają wskazać dane które mają być przekazane do strumienia tu przekazujemy tekst (napis). Napis musi być ujęty w cudzysłowy polecenie przesyłania danych musi być zakończone średnikiem przekierowania można sklejać : std :: cout << " Dzien dobry! \n" << "To ja" ;
Znaki specjalne w tekście std :: cout << " Dzien dobry! \n" ; std :: cout << "To ja, \t \t Twoj pierwszy \" program \" "; W tekście można umieszczać znaki specjalne. Najczęściej używane to: \n - przejście do nowej linii \t - znak tabulacji \" - znak cudzysłowu Efekt przejścia do nowej linii można uzyskać również przesyłając na standardowe wyjście znak oznaczony std::endl
Przykładowy program - drobna zmiana # include < iostream > using namespace std ; // okreslenie przestrzeni nazw int main () { cout << " Dzien dobry! " << endl ; cout << " To ja, Twoj pierwszy program "; } return 0;
Przestrzenie nazw using namespace std ;... cout << " Dzien dobry! " << endl ; Powyższa linia mówi że w programie poniżej niej będziemy używać nazw ze standardowej przestrzeni nazw - std nie musimy zatem poprzedzać nazw cout i endl przedrostkiem std:: - kompilator wie gdzie poszukiwać danego elementu
Identyfikatory w C++ Nazwy wykorzystywane w programie, (tzw. identyfikatory) muszą być ciągami znaków złożonymi z liter, cyfr i znaków podkreślenia, zaczynającymi się od litery, przy czym znak podkreślenia traktowany jest jak litera. polskie znaki nie są traktowane jako litery! wielkie i małe litery są rozróżniane zazwyczaj rozróżniane są tylko pierwsze 32 znaki Przykłady poprawne: prog1, prog 1, moja zmienna, A123, JamesBond007, moj program niepoprawne: on&ona, 1szy program, max%
Słowa kluczowe w C++ Niektóre identyfikatory są tzw. słowami kluczowymi (zarezerwowanymi). Nie mogą one pojawiać się w programie w innym znaczeniu niż nadane w standardzie języka. Lista słów kluczowych: https://pl.cppreference.com/w/cpp/keyword
Liczby Korzystanie z liczb C++ zna różne rodzaje liczb (różne typy liczbowe) i potrafi wykonywać na nich różne operacje arytmetyczne.
Podstawowe działania na liczbach całkowitych # include < iostream > using namespace std ; int main () { cout << " 20 + 2 = " << 20+2 << endl ; cout << " 20-2 = " << 20-2 << endl ; cout << " 20 * 2 = " << 20*2 << endl ; cout << " 20 / 2 = " << 20/2 << endl ; cout << " 20 / 3 = " << 20/3 << endl ; cout << " 20 % 2 = " << 20%2 << endl ; cout << " 20 % 3 = " << 20%3 << endl ; } return 0; użyte operatory arytmetyczne: + - dodawanie, - odejmowanie, - mnożenie, / - dzielenie, % - modulo (reszta z dzielenia)
Podstawowe działania na liczbach rzeczywistych Liczby rzeczywiste zapisujemy z kropką dziesiętną (przecinek zamiast kropki nie jest dozwolony!!) # include < iostream > using namespace std ; int main () { cout << " 20 + 2.0 = " << 20+2.0 << endl ; cout << " 20-2.0 = " << 20-2.0 << endl ; cout << " 20 * 2.0 = " << 20*2.0 << endl ; cout << " 20 / 2.0 = " << 20/2.0 << endl ; cout << " 20 / 2.1 = " << 20/2.1 << endl ; } return 0; operatory arytmetyczne: + - dodawanie, - odejmowanie, - mnożenie, / - dzielenie (nie ma operatora modulo!)
Dzielenie dzielenie dwóch liczb całkowitych daje wynik będący liczbą całkowitą (wykonywane jest dzielenie całkowite - 20/2 daje 10, 20/3 daje 6) jeśli przynajmniej jedna z liczb jest rzeczywista, wynik jest liczbą rzeczywistą... zatem jeśli chcemy uzyskać poprawny wynik dzielenia, musimy przynajmniej jedną z liczb zapisać z kropką dziesiętną (możliwe zapisy: 20.0/3 albo 20/3.0 albo 20.0/3.0)
Literały Literały są rodzajem stałych (wyrażeń o ustalonej, niezmiennej wartości). Umożliwiają umieszczenie w kodzie źródłowym programu konkretnych wartości (liczb, znaków, napisów), reprezentujac je za pomocą odpowiedniej notacji. Literały numeryczne reprezentują wartości liczbowe. Wyróżniamy literały całkowite reprezentujące wartości całkowite oraz literały zmiennoprzecinkowe (ang. floating-point literals) reprezentujące wartości rzeczywiste.
Literały całkowite liczby dziesiętne: 12, -12, 1234 liczby ósemkowe (system o podstawie 8): 012, -012 (cyfry liczby są poprzedzone zerem) liczby szesnastkowe (system o podstawie 16): 0xab, 0x12, -0X12, -0XAB (cyfry liczby są poprzedzone znakami 0x albo 0X, cyfry w tym systemie to 0..9, a..f) liczby binarne (system o podstawie 2) - zapis dozwolony od standardu C++14: 0B001, -0b1111 (cyfry liczby poprzedzone są znakami 0b albo 0B)
Literały całkowite - cd Domyślnym typem literałów całkowitych nie mających przyrostka specyfikującego typ jest int. Przyrostki umożliwiające przypisanie literału całkowitego do innego typu liczbowego to: (o typach za chwilę) u lub U typ unsigned int l lub L typ long int ll lub LL typ long long int (przyrostki można [odpowiednio] łączyć - np. ul oznacza unsigned long int. Przykłady: 75l, 75ul, 75LL).
Literały rzeczywiste Reprezentują liczby rzeczywiste, z kropką dziesiętną i/lub wykładnikiem. notacja standardowa : 12.3, -4.5 notacja naukowa (wykładnicza) : -3.3e5, 4.6E-2 domyślnym typem literałów rzeczywistych jest double. Przyrostki umożliwiające przypisanie literału rzeczywistego do innego typu rzeczywistego: f lub F typ float l lub L typ long double
Typy liczbowe W języku zdefiniowane są pewne (tzw. predefiniowane) typy liczbowe. Podstawowe to: int, short int, long int, long long int służące do reprezentowania liczb całkowitych; każdy z nich może być ze znakiem (signed, domyślnie) lub bez znaku (unsigned). Przykład: unsigned short int - typ krótki całkowity bez znaku (dozwolone jest używanie skróconych nazw typów: short, long, long long ) float, double i long double służące do reprezentowania liczb rzeczywistych
Zakresy typów Wartości danego typu T reprezentowane są w pamięci komputera przy użyciu pewnej, ustalonej dla danego typu, liczby bitów (jako ciągi zer i jedynek). Z tego powodu każdy typ ma swój zakres - istnieje w nim wartość najmniejsza i największa
Wyświetlenie zakresu typu int # include < iostream > # include < limits > using namespace std ; int main () { cout << " zakres int : od " << std :: numeric_limits < int >:: min () << " do " << std :: numeric_limits < int >:: max () << endl ; cout << " rozmiar wartosci typu int w bajtach : " << sizeof ( int ) << endl ; } return 0; Zakresy innych typów liczbowych można uzyskać w podobny sposób
Zmienne Aby móc zapamiętać (przechować) w pamięci komputera wprowadzoną z klawiatury albo wyliczoną w programie wartość pewnego typu, musimy zdefiniować zmienną tego typu (ang variable) w tym celu piszemy typ zmiennej nazwa zmiennej ; (np. int a; ) nazwy zmiennych muszą być indentyfikatorami (nie będącymi słowami kluczowymi) wynik: w odpowiednim momencie uruchamiania programu w pamięci zostaje zarezerwowany pewien obszar o rozmiarze odpowiednim dla przechowania wartości danego typu. Odwołujemy się do niego przez nazwę zmiennej. można zdefiniować naraz kilka zmiennych tego samego typu: int a,b,c;
Definicja a deklaracja deklaracja informuje kompilator że dana nazwa jest już znana, ale nie powoduje alokacji (rezerwacji) pamięci (przykład: extern int a; - informuje że zmienna a jest zdefiniowana w innym pliku będącym częścią programu) definicja określa dokładnie czym jest dany identyfikator. Zdefiniowanie zmiennej skutkuje zarezerwowaniem pamięci dla tej zmiennej Każda definicja jest deklaracją, ale nie każda deklaracja jest definicją.
Przypisanie Wartość zmiennej może ulegać zmianie w trakcie działania programu Operator przypisania (=) pozwala nadać zmiennej pewną wartość przypisywana wartość musi oczywiście mieścić się z zakresie typu do którego należy zmienna zmienna, której nie przypisano w programie żadnej wartości, ma wartość przypadkową
Przykład (Użycie instrukcji przypisania) deklaracje zmiennych: int a,b,i; i = 2; a = 2+2; b = a+i; weź wartość zmiennej a, dodaj do niej wartość zmiennej i, otrzymany wynik przypisz zmiennej b a = a + 5; weż wartość zmiennej a, dodaj do niej 5, otrzymany wynik przypisz z powrotem zmiennej a
Przykład użycia zmiennych # include < iostream > using namespace std ; int main () { int a; int b,c; a =3; b =6; c = a+b; cout << " a = " << a << endl ; cout << " b = " << b << endl ; cout << " a + b = " << c << endl ; } return 0;
Inicjalizacja zmiennych Zmiennej możemy nadać wartość już w momencie jej definiowania (inicjalizacja zmiennej). Przypisywana wartość powinna mieścic się w zakresie typu. Postać: typ zmiennej nazwa zmiennej = wyrażenie; Przykłady int i = 0; float s= 2.5234 + 3.234; int k, l=100, m; tylko jedna zmienna ma nadaną wartość początkową 100 powyższe wartości początkowe mogą być oczywiście zmienione w trakcie działania programu
Przykład (inicjalizacja zmiennych,oraz C++ to nie Excel ;-) ) # include < iostream > using namespace std ; int main () { int a = 3; int x, b =10; int c = a* b; } cout << " niezainicjalizowany x = " << x << endl ; a = 6; cout << " a = " << a << " b = " << b << " c = " << c << endl ; /* wartosc zmiennej a zmieniona, wartosc c bez zmiany ( nadal 30) */ return 0;