wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · romualda...

156
Wykład 1

Upload: duongnguyet

Post on 28-Feb-2019

221 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

Wykład 1

Page 2: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

Słowo informatyka, jako nazwa dyscypliny nauki, zostało zaproponowane przez profesora Romualda Marczyńskiego w roku 1968.

teoria informacji algorytmy i struktury danych programowanie i języki programowania konstrukcja komputerów i podzespołów inżynieria oprogramowania sztuczną inteligencja sieci komputerowe i komunikacja

bazy danych przetwarzanie równoległe/rozproszonym interakcje człowiek-komputer grafika komputerowa systemy operacyjne obliczenia numeryczne i algebraiczne

Technologia informatyczna – nauka o przechowywaniu, zabezpieczaniu i przesyłaniu

informacji. Computer science – technologia obliczeń – o użyciu maszyn do obliczeń. Inżynieria informatyczna – metodyka ludzkiej działalności: projektowania, tworzenia i

eksploatacji systemów informatycznych. Inżynieria oprogramowania – wytwarzanie oprogramowania.

Page 3: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

1952 – firma IBM opisała swój komputer IBM 701 jako mogący zastąpić stu pięćdziesięciu inżynierów z suwakami logarytmicznymi1 – na sekundę 2200 operacji zmiennoprzecinkowych2

2010 – ZEUS (Akademia Górniczo-Hutnicza w Krakowie) – 151 pozycja na świecie – 105 TFLOPS3 (ponad sto trylionów obliczeń zmiennoprzecinkowych na sekundę) – jest od IBM 701 szybszy ponad 47 miliardów razy – liczba „inżynierów z suwakami” przekracza ponad tysiąckrotnie liczbę wszystkich ludzi na Ziemi Jeden miliard operacji zmiennoprzecinkowych na sekundę to… 68 milionów inżynierów

1 http://www.globalnerdy.com/2009/08/10/old-ibm-ad-150-extra-engineers/, 23 lipca 2011

2 http://www-03.ibm.com/ibm/history/exhibits/701/701_1415bx01.html, 23 lipca 2011

3 http://www.nauka.gov.pl/nauka/sukcesy-uczonych/sukcesy-uczonych/artykul/superkomputer-zeus-

z-agh-najmocniejszy-w-polsce/, 23 lipca 2011

Page 4: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

Algorytmy

Główne paradygmaty programowania:

imperatywny – jakie czynności mają być zrobione? deklaratywny – co ma być zrobione? funkcjonalny – jak dane wpływają na wyniki? obiektowy – co stworzyć?

Algorytm możemy zdefiniować jako efektywną metodą osiągnięcia celu, przedstawioną jako skończony zapis dobrze określonych instrukcji. Przez metodą efektywną rozumiemy taką metodę która – jeżeli to konieczne – dla pewnej klasy problemów, daje rozwiązania zawsze, wyłącznie poprawne, w skończonej liczbie kroków. Dobrze określoną instrukcją jest instrukcja zrozumiała. Zrozumiałość należy pojmować jako zrozumiałość dla czytającego. Czytającym może być programista, a skutkiem czytania zapisanie algorytmu w innej formie, w szczególności jako implementacji w wybranym języku programowania.

Page 5: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

Zwykle programy są implementacjami bardzo nieskomplikowanych algorytmów, w oczywisty sposób wynikających z tego co chcemy osiągnąć. Jeżeli dla � krotnie razy większej, dostatecznie dużej, ilości danych trzeba wykonać � krotnie razy większą pracę, to asymptotyczną złożoność obliczeniową określamy jako �(�). Możemy wyróżnić algorytmy o złożoności stałej, logarytmicznej, liniowej, wielomianowej, podwykładniczej, wykładniczej i podwójnie wykładniczej, a nawet nadwykładniczej. Klasyfikacja jest niekompletna i opiera się o postać funkcji � = �(�), odpowiednio 1, ln�,

�, �, ��� , � ���

, � , gdzie stałe �, �, � są większe niż jeden. Podobnie oszacowywać możemy ilość pamięci niezbędnej dla danego algorytmu, lecz w praktyce wyróżnia się tylko algorytmy in situ. Jeżeli czas obliczeń jest proporcjonalny do 10 , to 100 razy większe � wymagać będzie 10��� szybszego komputera. Tylokrotny wzrost wydajności hardware nie jest możliwy.

Page 6: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

UWAGA!

Zanim zaczniemy pisać jakikolwiek program powinniśmy sprawdzić:

czy jest już może gotowy taki program, dostępny dla nas i spełniający nasze oczekiwania?

Jeżeli tak, to powinniśmy znaleźć uzasadnienie: dlaczego chcemy tworzyć taki sam program – zamiast robić coś ciekawszego? Technika programowania sekwencyjnego – polega na ponumerowaniu kroków naszego powstającego algorytmu i stosowanie dwóch prostych reguł:

- normalnie przechodzimy zawsze do kroku następnego. - możemy przejść niezgodnie z regułą pierwszą

W praktyce zapis z instrukcjami skoków może być skrajnie zagmatwany i bezładny, zupełnie nieczytelny nawet dla jego twórcy. Oczywiście nie ma bezpośredniego związku pomiędzy ładem w notacji a logiką działania programu. Jednakże zachowanie porządku ułatwia kontrolę, obniża koszty itd., a tym samym pośrednio zwiększa prawdopodobieństwo poprawności działania programu.

Page 7: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie
Page 8: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

Aby odkryć sposób na najlepszy sposób programowania zastanówmy się jakie, zupełnie ogólnie, mogą być algorytmy. Algorytmy proste (sekwencyjne), są po prostu spisem kroków, które należy kolejno wszystkie wykonać. Algorytmy złożone (rozgałęzione) wymagają podejmowania decyzji, które kroki mają być wykonane, a które nie. Algorytmy rekurencyjne to algorytmy w których przynajmniej jednym z kroków jest dany algorytm, a gdy jest to wyłącznie krok ostatni mówimy o rekurencji prawostronnej.4 Algorytmy iteracyjne są to algorytmy wymagające powtarzania określonych kroków

4 Przykład algorytmu rekurencyjnego: �-tej liczba Fibonacciego to suma � − 1 liczby Fibonacciego i � − 2 liczby Fibbonaciego; dwie pierwsze liczby, dla � = 0 i dla � = 1, to 0 oraz 1.

Page 9: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

Technika programowania strukturalnego, opisanej przez Böhma i Jacopiniego,5 umożliwiającej implementację dowolnie skomplikowanego algorytmu w postaci struktury złożonej właśnie z sekwencji, rozgałęzień i iteracji oraz (w połączeniu z techniką programowania proceduralnego) rekurencji. Instrukcje skoków są zbędne a programy lepiej uporządkowane i bardziej czytelne. Ponadto programowanie strukturalne uzasadnia ograniczenie liczby instrukcji sterujących do instrukcji rozgałęzienia i instrukcji iteracji. Dla programowania proceduralnego trzeba uzupełnić instrukcje sterujące o możliwość wywoływania podprogramów i wprowadzić zmienne lokalne. Chociaż stosowane jako równoznaczne są takie określenia jak podprogram, procedura i funkcja, to funkcjami powinno się nazywać tylko te procedury nie posiadające efektów ubocznych, które dają zawsze takie same rezultaty gdy są wywoływane z tymi samymi argumentami. Takie rozumienie funkcji jest zgodne z paradygmatem funkcyjnym.

5 Corrado Böhm, Giuseppe Jacopini, Flow diagrams, Turing machines and languages with only two

formation rules, Commun. ACM 9, 5 (1966), 366-371.

Page 10: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

Podsumowując: to co robi hardware określane jest przez software, software to programy będące implementacjami algorytmów,

a rolą programisty jest stworzyć algorytmy i zapisać je, za pomocą instrukcji sterujących,

do dalszego automatycznego przetworzenia.

Page 11: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

Maszyna Turinga i języki programowania

Przełomowym rokiem dla rozwoju języków programowania był rok 1936, w którym matematyk brytyjski Alan Turing wymyślił teoretyczną maszynę, przetwarzającą sama dla siebie rozkazy zapisane jako ciąg instrukcji umieszczonych na taśmie. Maszyna miała pobierać rozkaz z taśmy, przetwarzać go, umieszczać na taśmie przetworzoną wartość i przechodzić do innego, być może kolejnego, rozkazu.6 Dla matematyków najważniejsza była możliwość udowodnienia, czego nie może zrobić maszyna Turinga (nie może rozstrzygać poprzez indukcję pozaskończoną). Dla informatyków, że polecenia dla maszyny to też dane. Konsekwencją jest możliwość zapisania programu w przyjaznej postaci i automatycznego przetłumaczenia – bez udziału człowieka – go na postać instrukcji sterujących maszyną.

6 http://vimeo.com/44202270, 5 października 2012

Page 12: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

Gdyby komputery miały inteligencję pozwalającą zrozumieć język naturalny, to polecenia dla nich moglibyśmy wydawać po prostu mówiąc do nich tak jak do ludzi. Tak (na razie jeszcze) nie jest: używa się sztucznych, uproszczonych i sformalizowanych, języków programowania. W większości języków programowania istnieje pewna, niewielka, liczba predefiniowanych identyfikatorów określanych jako słowa kluczowe. Słowa kluczowe są zwykle słowami języka angielskiego oznaczającymi bardzo proste fragmenty rozkazów, takie jak:

zacznij, zakończ, zrób, jeżeli, przerwij, idź do itd. czyli

begin, end, do, if, break, goto etc.

Oprócz słów kluczowych, używa się różnych znaków, takich jak !@#$%^&*<=>/+-?, oraz

nawiasów i znaków oddzielających (przecinek, średnik, dwukropek itp.)

Operatory maja jeden, dwa lub trzy argumenty, najczęściej stosowana jest notacja infiksowa,

choć możliwe jest także prefiksowa i postfiksowa (czyli notacja polska). W niektórych

językach są operatory złożone z więcej niż jednego znaku, niektóre języki jako operatory

traktują słowa takie jak and or not.

Page 13: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

Podobnie jak w przypadku języków naturalnych języki programowania mają gramatykę (rozumianą jako zbiór reguł tworzenia poprawnych wypowiedzi). Reguły składni określają jakie z elementów języka budować programy: co, gdzie i w jakiej kolejności może być w programie. Odstępstwa od prawidłowej składni są automatycznie wychwytywane przez kompilatory i dlatego nie stanowią zagrożenia, podobnie jak błędy „ortograficzne” (np. napisanie zamiast return słowa retórn przez o-z-kreską), które także traktowane jako błędy składni. Prawdziwe groźne problemy? Błędy semantyczne! Czyli zupełnie poprawne pod względem formalnym zapisanie niewłaściwych instrukcji. Na przykład, jeżeli chcemy zwiększyć zmienną licznik o jeden i napiszemy instrukcję

licznik := licznik – 1;

to kompilator nie ma jakiejkolwiek możliwości odgadnięcia, że jest to błędne użycie zamiast znaku plus znaku minus.

Page 14: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

Asembler

Piszemy w nim wprost rozkazy dla konkretnego procesora – ale używamy zamiast cyfr mnemoników i innych udogodnień (w tym komentarzy), na przykład dla procesora Zilog Z80 trzy kolejno wydane rozkazy:

xor ax ; wyzerowanie rejestru ax inc ax ; zwiększenie wartości rejestru ax o jeden ld h,a

to właśnie po przetłumaczeniu AF 3C 67 w zapisie hexadecymalnym, a w zapisie dwójkowym 10101111 111100 1100111. Asembler i rozkazy procesorów są bardzo ważne, bo ich znajomość jest konieczna do stworzenia bardziej zaawansowanych języków wysokiego poziomu. Programowanie w asemblerze jest sprzeczne z ludzką naturą i nie zapewnia wystarczającego tempa prac nad programami, utrudnia wprowadzanie jakichkolwiek modyfikacji, prowadzi do programów nieprzenośnych (program napisany z przeznaczeniem dla określonego procesora zwykle nie będzie działał na innym sprzęcie).

Page 15: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

Języki wysokiego poziomu:

Pierwszym7 popularnym językiem programowania wysokiego poziomu (niezależnym od konkretnego procesora) był język Fortran

8, stworzony w latach 1953-1957 przez zespół kierowany przez Johna Backusa, przeznaczony dla komputerów IBM 704. Fortran, po wielu ulepszeniach, używany jest do dziś jako jeden z najbardziej wydajnych w obliczeniach języków programowania. Fortran nie nadaje się do programowania przetwarzania tekstów, baz danych, operacji finansowych, obsługi interfejsów graficznych, witryn internetowych, gier komputerowych, sterowników urządzeń i systemów operacyjnych. Pisanie takich programów w języku Fortran nie jest niemożliwe, lecz po prostu trudne, żmudne, nieefektywne, wymagające wywoływania pisanych w Asemblerze fragmentów.

7 Przed Fortranem powstały takie języki programowania jak np. Plankalkul i A-0.

8 FORmula TRANslator

Page 16: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

Algol – ALGOrytmic Language; istnieją trzy jego odmiany, Algol 58, Algol 60 i Algol 68 Cobol – COmmon Business Oriented Language; ulepszany w 1968, 1974, 1985, 2002; doczekał się nawet wprowadzenia możliwości programowania obiektowego Basic – Beginner's All-purpose Symbolic Instruction Code (rok 1964), zachęca do złych praktyk i nie nadaje się do obliczeń numerycznych Pascal - opracowany przez Niclausa Wirth’a w roku 1970. Od Pascala i Algolu wywodzą się takie języki programowania jak Modula i Ada, Delphi, Oxygene oraz wiele innych. Z Algolu 60 wywodzi się także język CPL

(Combined Programming Language) który został zastąpiony przez BCPL (Basic Combined Programming Language), który z kolei był inspiracją do stworzenia języka B.

Page 17: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

Kolejną literą po B jest C i stąd nazwa jednego z najpopularniejszych języków programowania. Popularność języka C, stworzonego przez Dennisa Ritchiego, wynika z dwóch faktów:

Jest to dobry język sprawdzający się w praktyce. Jest to język w którym napisano9 systemy operacyjne Unix i Linux, Microsoft Windows i OS X oraz Android.

Jeżeli potrzebny jest jakiś język programowania, to niemal zawsze mamy możliwość użycia C, niezależnie od tego czy piszemy program dla superkomputera czy dla mikrokontrolera wycieraczek.

9 Niektóre części napisano w asemblerze, niektóre w językach takich jak C++ i Objective-C.

Page 18: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

Obiektowymi językami wywodzącymi się z C są Objective-C, C++, C# oraz C--. Zbliżonym do niego językiem jest niszowy AWK, język C++ jest też jednym z przodków języków Java i Java

Script. Oczywiście, języków programowania jest dużo więcej, niektóre z nich są podobne do Algolu, inne do C albo Basica. Są też języki od tamtych różne, takie jak Forth, G i Kepler, Lisp, Lua, Logo, PHP, Phyton, Prolog, Ruby, Tcl. Są wyspecjalizowane języki/środowiska, przykładami są Matlab i Mathematica (obliczenia numeryczne i symboliczne) oraz POV-ray (foto-realistyczne wizualizacje), dedykowane do ściśle określonych zadań. Są języki skryptowe, takie jak język skryptów powłoki bash, który jest przeznaczony do automatyzacji wydawania poleceń systemowi Unix (istnieje też wariant dla MS Windows i oczywiście dla Linuksa) oraz VBScript i PowerShell (MS Windows). Natomiast języków Postscript, TeX, SGML, HTML i XML oraz SQL zazwyczaj nie zalicza się do języków programowania, choć są bardzo często używane. Odwrotnie, języków ezoterycznych (m.i. Brainfuck i Malbolge, DNA#) nie używa się niemal wcale, bo są one – same w sobie – dziełami sztuki.

Page 19: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

Istnieje jeszcze jeden „język programowania” nazywany pseudokodem. Nie jest to język przeznaczony dla komputerów, ale po prostu zapis algorytmów w języku naturalnym10 – z ewentualnymi elementami zapożyczonymi z języków programowania – zrozumiały wyłącznie dla ludzi, na przykład:

wczytaj parametry

wykonaj obliczenia

jeżeli n > 1 to pokaż wyniki

Podobnie, opis algorytmów i architektury oprogramowania jest możliwy przez diagramy blokowe (flowchart) oraz schematy takie jak UML

11 i LePUS3,12 ale obecnie trudno je uznać za „prawdziwe języki programowania”.

10 Pomysł stworzenia języka LiveCode (dawniejsze Revolution) opiera się na programowaniu w języku

zbliżonym do języka naturalnego. 11

Unified Modeling Language 12

Language for Pattern Uniform Specification 3

Page 20: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

Jeżeli chcemy poznać konkretnie jeden język programowania, to najlepiej jeżeli będzie to C/C++: w obliczeniach numerycznych jest nawet szybszy niż Fortran, można w nim programować mikrokontrolery, nadaje się do przetwarzania różnych danych i sterowania automatyką, można w nim pisać wielkie systemy operacyjne i małe proste programiki, kompilacja do natywnego kodu nieźle zabezpiecza przed inżynierią odwrotną a pod względem popularności13 jest w ścisłej czołówce jako numer 1. Alternatywnym wyborem jest język Java, pod wieloma względami lepszy, pod wieloma innymi gorszy.14 Język C jest najprostszy z tej trójki do opanowania, bo jest językiem nieobiektowym (i jednocześnie najgorzej z nich nadaje się do pisania bardzo długich i rozbudowanych programów).

13 Ranking TIOBE Programming Community Index, http://www.tiobe.com, 14 lipca 2012.

14 Wadami Javy jest: monopol firmy Oracle, wielokrotnie mniejsza prędkość działania programów,

zbyt mała odporność na reverse engineering. W Javie nie można redefiniować operatorów i używać dziedziczenia wielobazowego, nie używa się makroprocesora, nie ma arytmetyki wskaźników, parametry można przekazywać wyłącznie przez wartość. Nie istnieje standard Javy. Zwyczaj powiązania przestrzeni nazw pakietów Javy z domenami internetowymi jest obecnie niepraktyczny.

Page 21: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

Wykład 2

Page 22: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

Nicnierobienie

int main(void) { return 0; }

Słowo main zawsze oznacza ten fragment programu, od którego zaczyna się wykonanie instrukcji napisanych w języku C. Jest to po prostu pewna funkcja15 typu int. Za słowem main są dwa nawiasy okrągłe - takie nawiasy właśnie wyróżniają nazwy funkcji od innych nazw w programie. Słowo kluczowe void oznacza, że funkcja main nie ma argumentów. Nawiasy klamrowe grupują instrukcje jakie funkcja ma wykonać. Każda instrukcja kończy się średnikiem, a instrukcja return to nakaz zakończenia funkcji i zrócenia określonej wartości. W języku C wielkość liter ma znaczenie – małe litery są traktowane jako zupełnie różne od wielkich – nie można więc napisać RETURN zamiast return. Program jest zgodny ze standardem ISO 9899:1999 (popularnie określanym jako C99).

15 W języku C pojęcie funkcja oznacza to co wielu innych językach określa się jako funkcję, procedurę

lub podprogram. Dlatego czasem będę używać określenia procedura na funkcję C zwracającą void.

Page 23: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

Stosując Microsoft Visual Studio 2010 (10.0.30319.1 RTMRel) pracujące w systemie Microsoft Vista działającym na komputerze z procesorem Intel Core Duo T7300 otrzymamy następujący efekt kompilacji wybierając opcję release:

1: int main(void) 2: { 3: return 0; 001C1000 33 C0 xor eax,eax 4: } 001C1002 C3 ret

Te 3 bajty kodu maszynowego połączone z kodem startowym i bibliotekami, po dowiązywanu zostały zasobów i manifestu dają formacie .NET PE 6144 bajty pliku EXE. Całkiem sporo jak na nicnierobienie.16

16 Dla porównania: komputer ZX80 z roku 1980 miał całą pamięć operacyjną (RAM) o wielkości

zaledwie 1024 bajtów i 4096 bajtową pamięć ROM.

Page 24: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

Jak zrobić to samo w C++? Kompilator, jeżeli będziemy mieli szczęście, nawet nie zauważy że program nie jest w C++ tylko w C-bez-plusów. Jeżeli jednak chcemy być ultrapoprawni i trzymać się standardu ISO/IEC 14882:2011, to powinniśmy wyrzucić z programu słowo void:

int main() { return 0; }

bo w C++ puste nawiasy () oznaczają brak argumentów, a w C nieokreślone argumenty. Dyskusja nad wyższością pustych nawiasów nad nawiasami z void nie ma jednak sensu – większość programów zaczyna się bowiem albo poprawnym a identycznym dla C i C++ wywołaniem

int main(int argc, char* argv[])

albo jakimś niezgodnym ze standardem, ale takim które akurat jest potrzebne i dobrze

działa.

Page 25: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

Dla systemu MS Windows, jeżeli piszemy programy używając Win API, może to być:

int CALLBACK WinMain(HINSTANCE hI, HINSTANCE hP, LPSTR lpCmd, int nCmdShow)

Trochę inaczej sprawy wyglądają w przypadku C# i Javy. W obu tych językach musimy

zdefiniować klasy i dopiero w tych klasach mamy statyczne metody main:

class Nicnierobienie // przykład w C# { static void Main(string[] args) { } }

public class Nicnierobienie // przykład w Javie { public static void main(String[] args) { } }

Page 26: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

Czytanie i pisanie

Przeczytaj liczbę

Oblicz jej sinus

Wypisz obliczoną wartość

/************************************************************************ * Program sinus, wersja 0.1, © 2011 Sławomir Marczyński * * Przykład programu który czyta liczbę, oblicza coś i wypisuje wynik. * * Można go używać np. wywołując go na konsoli: * * sinus <input.txt >output.txt * * Wszystko to co jest pomiędzy parami znaków ukośnik-gwiazdka i gwiazdka * -ukośnik jest komentarzem, więc nie jest czytane przez kompilator. * Komentarz pozwala w program wstawiać dowolne uwagi, objaśniać co jest * robione, zapisywać z kodem źródłowym kto jest autorem programu itd. ************************************************************************/

Page 27: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

#include <stdio.h> /* dołączenie deklaracji funkcji wejścia wyjścia */ #include <math.h> /* dołączenie deklaracji funkcji sin */ int main(void) { /* Definicja dwóch zmiennych mających typ double czyli liczb zmienno- przecinkowych podwójnej precyzji (programiści piszący w C nie lubią dużo pisać). W języku C zmienne definiujemy zaraz na początku, po nawiasie klamrowym, chyba że mamy kompilator zgodny ze standardem C99, wtedy możemy deklarować je niemal w dowolnym miejscu programu. */ double a,b; scanf("%lf", &a); /* czytamy ze stdin, czyli standardowego wejścia */ b = sin(a); /* obliczamy wartość sinusa */ printf("%lf", b); /* wypisujemy na stdout, czyli standardowym wyjściu*/ return 0; }

Page 28: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

Oczywiście w języku C cały program sinus można byłoby zapisać na przykład tak:

#include <stdio.h> #include <math.h> int main(void){double a,b;scanf("%d",&a);b=sin(a);printf("%d",b);return 0;}

lecz jest to niezalecane, bo program staje się nieczytelny, nieestetyczny i łatwo wtedy o błędy. Czytelność kodu źródłowego programu zwiększamy umieszczając odpowiadające sobie nawiasy klamrowe są jeden pod drugim i stosując „wcięcia” oraz puste linie. Dodatkowo wstawiamy komentarze.

Komentarz jest pomijany przez kompilator w ten sposób, że wszystko co jest zawarte pomiędzy parami znaków /* … */ jest, włącznie z tymi parami znaków, zastępowane jednym znakiem odstępu (spacją).

Jeżeli napiszemy n = 48, to nie będziemy bez komentarza wiedzieć, czy oznacza to indeks elementu tablicy, numer kierunkowy do Polski czy liczbę zapałek w pudełku, a może jeszcze coś zupełnie innego. Jest tylko jedna rzecz gorsza niż brak komentarzy w programie: złe komentarze wprowadzające w błąd.

Page 29: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

Skoro C++ to udoskonalone C, to co zyskamy używając C++ w tak prostym przypadku?

/************************************************************************ * Program sinus, wersja 1.1, © 2012 Sławomir Marczyński * * Przykład programu w C++: czyta liczbę, oblicza coś i wypisuje wynik. * ************************************************************************/ #include <iostream> // dołączenie deklaracji funkcji wejścia/wyjścia #include <cmath> // dołączenie deklaracji funkcji sin using namespace std; int main() { double a; cin >> a; // czytamy ze cin, czyli standardowego wejścia double b = sin(a); // obliczamy wartość sinusa cout << b; // wypisujemy do cout return 0; }

Page 30: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

Flow control

Omawiając algorytmy określiliśmy ich podział. Jak to się ma do języka C ?

Algorytmy proste – sekwencje i bloki instrukcji Algorytmy rozgałęzione – instrukcja warunkowa i instrukcja wyboru Algorytmy rekursyjne – wywołania funkcji Algorytmy iteracyjne – instrukcje pętli while, do-while i for.

Technicznie możliwe jest użycie instrukcji skoków – goto – a nawet długiego skoku. Ale po prostu nie wypada tego robić – nigdy nie jest to konieczne, niezwykle rzadko daje jakiekolwiek korzyści, sprzeczne jest z ideą programowania strukturalnego.

Page 31: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

Jeżeli mamy do wykonania ciąg instrukcji, kolejno od instrukcji pierwszej do pewnej ostatniej, to moglibyśmy narysować diagram i zapisać w pseudokodzie

instrukcja1

instrukcja2

instrukcja3

instrukcja4

instrukcja1; instrukcja2; instrukcja3; instrukcja4;

Jeżeli mamy do wykonania ciąg instrukcji, kolejno od instrukcji pierwszej do pewnej w pseudokodzie a następnie w C:

Page 32: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

Bardzo ważna jest w języku C możliwość grupowania instrukcji w bloki, polega to na umieszczeniu ciągu instrukcji w nawiasach klamrowych. Blok instrukcji jest traktowany z zewnątrz jako jedna instrukcja.

{ instrukcja1; instrukcja2;} { instrukcja3; instrukcja4;}

Jest to podobne do użycia w Pascalu słów kluczowych begin i end.

Bardzo ważna jest w języku C możliwość grupowania instrukcji w bloki, polega to na Blok instrukcji jest traktowany z

instrukcja1; instrukcja2;

instrukcja3; instrukcja4;

Page 33: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

Instrukcja warunkowa pozwala wybrać nam jedną z dwóch możliwości:

Jeżeli będzie spełniony

to instrukcja1

w przeciwnym razie

instrukcja2

if (warunek) instrukcja1;else instrukcja2;

pozwala wybrać nam jedną z dwóch możliwości:

Jeżeli będzie spełniony warunek

to instrukcja1

w przeciwnym razie

instrukcja2

instrukcja1;

a2;

Page 34: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

Możliwa jest konstrukcja warunkowa w której decydujemy o wykonaniu tylko jednej instrukcji: wariant bez else

Jeżeli będzie spełniony warunek

to instrukcja1

w przeciwnym razie

instrukcja2

if (warunek) instrukcja1;

w której decydujemy o wykonaniu/niewykonaniu

spełniony warunek

to instrukcja1

w przeciwnym razie

instrukcja2

instrukcja1;

Page 35: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

Ma ona, bardzo rzadko spotykaną, odmianę „z else” – w której zostawiamy wyłącznie instrukcję po else.

Jeżeli będzie spełniony warunek

to instrukcja1

w przeciwnym razie

instrukcja2

if (warunek) ; else instrukcja2;

w której zostawiamy wyłącznie

Jeżeli będzie spełniony warunek

to instrukcja1

w przeciwnym razie

instrukcja2

instrukcja2;

Page 36: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

Możliwe jest stosowanie instrukcji warunkowych jednej wewnątrz drugiej, tak

rozgałęziony algorytm pokazany na diagramie poniżej:

Możliwe jest stosowanie instrukcji warunkowych jednej wewnątrz drugiej, tak aby zapisać

Page 37: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

if (warunek1) { if (warunek2) { if (warunek4) instrukcja1; else instrukcja2; } else { if (warunek5) instrukcja3; else instrukcja4; } } else { if (warunek3) { if (warunek6) instrukcja5; else instrukcja6; } else { if (warunek7) instrukcja7; else instrukcja8; } }

Page 38: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

Niekiedy możemy opuścić pisanie nawiasów, np. taki zapis:

if (warunek1) instrukcja1; else if (warunek2) instrukcja2; else if (warunek3) instrukcja3; else instrukcja4;

jest równoważny (i jednocześnie być może łatwiejszy do zrozumienia) temu poniżej:

if (warunek1) instrukcja1; else { if (warunek2) instrukcja2; else { if (warunek3) instrukcja3; else instrukcja4; } }

Page 39: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

Warunkiem w instrukcji warunkowej jest każde wyrażenie które ma wartość true albo false. Przez wartość false rozumie się też wartość zero, przez wartość true rozumie się jakąkolwiek wartość różną od zera. Często spotykane warunki to na przykład: a > 0, a > b, a == b, a >= b oraz a != b. Zapis dwóch znaków równości obok siebie odróżnia porównanie od instrukcji podstawienia. Przykładowo, moglibyśmy wybrać większą z dwóch wartości zapisanych w zmiennych a,b sprawdzając czy a > b, aby potem zapisać ją w zmiennej c.

if (a > b) c = a; else c = b;

Bardziej skomplikowane warunki można tworzyć używając nawiasów okrągłych i operatorów logicznych: negacji ! alternatywy || koniunkcji && . Wyrażenia takie są ewaluowane, od lewej do prawej z uwzględnieniem nawiasów, tylko w takiej części, która jest niezbędna.

if ( (a > 0 && b > 0) || ( a >= b && !find() ) )

Page 40: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

Ponieważ w języku C zero oznacza fałsz, nie-zero prawdę, a instrukcja przypisania może być użyta wewnątrz wyrażeń, to powszechnie stosowane jest umieszczanie instrukcji podstawienia wewnątrz warunku. W ten sposób można np.

b = a; if ( b != 0 ) printf("ok");

można uprościć do

if ( ( b = a ) != 0 ) printf("ok");

Ale choć formalnie poprawne jest zapisanie tego jeszcze krócej

if ( b = a ) printf("ok");

to nie należy tak robić, taki zapis bardzo łatwo pomylić ze zwykłym porównaniem.

Page 41: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

Język C ma możliwość skróconego zapisu instrukcji warunkowej obliczającej wartość. Jest to operator trójargumentowy ?: Można go użyć, gdy w wyrażeniu wybierana jest jedna z dwóch wartości – inna w przypadku true, innej w przypadku false. Na przykład jeżeli chcemy, aby w zmiennej c umieścić największą z dwóch wartości jakie są w zmiennych a i b, to możemy użyć wyrażenia z operatorem trójargumentowym:

c = (a > b) ? a : b ;

zamiast normalnej instrukcji warunkowej

if ( a > b ) c = a; else c = b;

Ponieważ nadużywanie operatora trójargumentowego prowadzi do nieczytelnego kodu źródłowego (przykład poniżej jest jeszcze mało skomplikowany):

c = (a > (q < p: 1 : a) ? (l+1 < m ? 0 : 1) : 2);

to niekiedy w firmach softwareowych są wewnętrzne zakazy stosowania go w programach.

Page 42: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

Instrukcja (wielokrotnego) wyboru – uwaga brak break ma poważne konsekwecje!

Sprawdź ile wynosi x, a potem jeżeli wynosi

1 – to wykonaj instrukcję instrukcję insA

2 lub 3 – to wykonaj instrukcję instrukcję insB

5 – to wykonaj instrukcję instrukcję insC i instrukcję insD

coś innego – to wykonaj instrukcję insX

switch (x) { case 1: insA; break; case 2, 3: insB; break; case 5: insC; insD; break; default: insX; }

Page 43: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

Zwykle na diagramach blokowych instrukcję switch rysuje się tak samo jak instrukcję wielokrotnego wyboru można zastąpić zwykłymi instrukcjami warunkowymi. Używanie switch ma znaczenie głownie estetyczne i być może pozwala kompilatorowi na lepszą optymalizację programu.

rysuje się tak samo jak if. Każdą instrukcję wielokrotnego wyboru można zastąpić zwykłymi instrukcjami warunkowymi.

być może pozwala kompilatorowi na

Page 44: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

Program sinus obliczał wartość sinusa podanej liczby i kończył działanie. „flowchart” można byłoby go przedstawić jako sekwencję kolejnych poleceń, bez rozgałęzień i bez zapętleń:

Ulepszmy go tak, aby liczył sinusy dla tylu liczb, ile zostanie ich wprowadzone do programu.

by i kończył działanie. Na schemacie „flowchart” można byłoby go przedstawić jako sekwencję kolejnych poleceń, bez rozgałęzień

Ulepszmy go tak, aby liczył sinusy dla tylu liczb, ile zostanie ich wprowadzone do programu.

Page 45: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

Powtarzaj tak długo jak długo uda ci się przeczytać kolejną liczbę

oblicz jej sinus

i wypisz obliczoną wartość

Potrzebujemy instrukcji nakazującej powtarzanie. W języku C są trzy takie instrukcje do wyboru: while, do-while i for. Możemy z ich pomocą tworzyć algorytmy iteracyjne

długo jak długo uda ci się przeczytać kolejną liczbę

Potrzebujemy instrukcji nakazującej powtarzanie. W języku C są trzy takie instrukcje do algorytmy iteracyjne.

Page 46: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

Pętla while sprawdza warunek „na początku”, ma ona postać:

Tak długo jak spełniony jest warunek

wykonuj pewną instrukcje

(lub pewien blok instrukcji)

while (warunek instrukcja

while (n > 0) // dopóty n będzie większe niż zero { n = n - 1; // zmniejsz o jeden wartość n m = 2 * m; // podwój wartość m }

Tak długo jak spełniony jest warunek

wykonuj pewną instrukcje

(lub pewien blok instrukcji)

warunek) instrukcja;

Page 47: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

Pętla do-while jest niemal identyczna, ale sprawdza warunek „na końcu”:

Wykonuj pewną instrukcje

tak długo jak spełniony jest

pewien warunek

do { instrukcja1; instrukcja2;} while (warunek

do { n = n - 1; // zmniejsz o jeden wartość n m = 2 * m; // podwój wartość m } while (n > 0) // powtarzaj jeżeli n będzie większe niż zero

while jest niemal identyczna, ale sprawdza warunek „na końcu”:

Wykonuj pewną instrukcje

tak długo jak spełniony jest

pewien warunek

instrukcja1; instrukcja2;

warunek) ;

// powtarzaj jeżeli n będzie większe niż zero

Page 48: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

Instrukcja for ma nieco odmienne zastosowania, choć w języku C różnic pomiędzy pętlą for i pętlą while jest znacznie mniej, niż w innych językach programowania, np. w Basic-u. Bardzo często potrzebna jest wielokrotne wykonywanie pewnego fragmentu programu, ale za każdym razem z inną, kolejną, wartością pewnej zmiennej. Gdy chcemy np. powtórzyć 10 razy działanie pewnej części programu, to wystarczy że wykonamy go dla zmiennej kontrolnej j równej kolejno 1, 2, 3, …, 10.

Dla zmiennej j równej początkowo 1,

za każdym razem zwiększanej o 1, aż do 10,

powtarzaj podany fragment programu

W języku C zapisujemy to zwięźle w postaci pętli for

for ( j = 1 ; j <= 10 ; j = i + 1 ) instrukcja;

Page 49: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

Instrukcja for w języku C ma znacznie więcej możliwości, bo z jej ogólnej postaci (przy tym można opuścić każdą część w nawiasie, a nawet wszystkie trzy naraz)

for (start; test; krok) coś_do_zrobienia;

wynika, że jest ona po prostu skrótowym zapisem pętli while

start;

while (test)

{

coś_do_zrobienia;

krok;

}

ma znacznie więcej możliwości, bo z jej ogólnej postaci (przy tym , a nawet wszystkie trzy naraz)

coś_do_zrobienia;

Page 50: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

Daje to możliwość tworzenia różnorodnych pętli.

for (j = 10; j <= 50; j = j + 10) { printf("%d\n", j); }

for (k = 50; k >= 10; k = k - 10) { printf("%d\n", k); }

for (j = 10, k = 50; j <= 50 && k >= 10; j = j + 10, k = k - 10) { printf("%d\n", j); }

for (j = 1, p = 1; j < 10; j = j + 1, p = 2 * p) { printf("%d\n", j); }

Page 51: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

Zgodnie z C99 można w języku C napisać:

for ( int j = 1 ; j ; j-- ) { printf("%d\n", j); }

dlatego że, podobnie jak w językach C++ i Java, definicje zmiennych można umieszczać w (niemal) dowolnym miejscu.

Zamiast j = j – 1 najczęściej jest stosowany operator dekrementacji zapisywany jako dwa minusy obok siebie.

Sprawdzanie warunku, czy j > 0, można zastąpić napisaniem po prostu j, bo gdy j będzie równe zero, to jednocześnie będzie miało wartość uznawaną za false.

UWAGA: jeżeli warunek, który musi być spełniony (aby pętla się wykonywała) będzie false choćby raz – to pętla zostanie przerwana i nie będzie wykonywana, nawet jeżeli dla kolejnych wartości warunek ten mógłby być true.

Page 52: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

Można opuścić każdą część instrukcji for, a nawet wszystkie trzy, na przykład

for ( ; j <= 50; j = j + 10) printf("%d\n", j);

Warto zauważyć, że w pętla for(;;), podobnie jak while(true) oraz do{}while(true) nigdy nie zakończą swojego działania - wyjście z takich pętli możliwe jest przez break, return i goto (oraz specjalne tzw. long jumps będące globalną wersję goto, przerwania i sygnały, a także wyłączenie zasilania).

for (i = 1; i < 10; i++) { putchar('*'); if ( i > 5) break; putchar('x'); }

for (i = 1; i < 10; i++) { putchar('*'); if ( i > 5) continue; putchar('x'); }

Page 53: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

Użycie instrukcji break jest podobne do skoku

for (i = 0; i < 10; i++) { putchar('*'); if ( i > 5) goto 1; putchar('x'); } 1:;

i dlatego należy unikać jej używania, jeżeli to nie jest konieczne. Instrukcja break nie pozwala wyjść z zagnieżdżonych pętli, dlatego poniższy fragment programu wypisuje więcej gwiazdek niż można byłoby się spodziewać:

for (i = 0; i < 10; i++) for (j = 0; j < 10; j++) { putchar('*'); if ( i + j > 5) break; }

Page 54: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

W językach Basic, Pascal itp. instrukcje sterujące są podobnie jak w C

x = 1 While 1+x > 1 Print x x = x / 2 Wend

s1 = 0 For k = 0 To n s1 = s1 + q ^ k Next s2 = 0 For k = n To 0 STEP -1 s2 = s2 + q ^ k Next

s1 := 0.0; p := 1.0; for k := 0 to n do begin s1 := s1 + p; p := p * q; end if x >= 0 then begin x := sqrt(x) end;

Pozwala to łatwo tłumaczyć programy napisane w tych językach na język C. Nie ma jednak w Pascalu odpowiedników break i continue.

Page 55: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

W językach C#, C++, Java i AWK są takie same instrukcje sterujące jak w języku C, z którego zostały zaczerpnięte. Dodatkowo wprowadzono instrukcję „foreach”, na przykład w językach C#, C++ i Java można napisać:

foreach(int liczba in lista) { Console.WriteLine(liczba); }

for (int& liczba : lista) { cout << liczba }

for (int liczba : lista) { System.out.println(x); }

Ponadto w języku Java instrukcja break działa nieco inaczej, bo można używać jej do wychodzenia z zagnieżdżonych pętli (przez co naśladuje ona nieobecne w Javie goto), a w C++ można zamiast zwykłych pętli używać wywołań STL.

Page 56: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

Sięgając do dokumentacji funkcji scanf, na przykład wpisując „man printf” w wyszukiwarkę Google17, dowiemy się, że funkcja ta zwraca ile liczb (albo innych danych) udało się jej przeczytać. Jeżeli czytamy jedną liczbę na raz, to wynikiem działania scanf() może być albo 1 gdy uda nam się przeczytać, albo 0 gdy nam się nie uda przeczytać. Dlatego nasz program może wyglądać na przykład tak:

/************************************************************************ * Program sinus, wersja 0.2, © 2011 Sławomir Marczyński * * Przykład programu który czyta liczby, oblicza coś i wypisuje wyniki. * * sinus <input.txt >output.txt * ************************************************************************/

17 Proste i skuteczne, ale w Linuksie można napisać man –k printf, a w środowiskach takich jak MS

Visual Studio po prostu nacisnąć F1 na zaznaczonej w edytorze nazwie pisanej funkcji; man to skrót od manual i jest programem do udostępniania dokumentacji w systemie Linux. Oczywiście, można sięgnąć także do papierowej dokumentacji, na przykład książki „Standard C Library” Plaugera.

Page 57: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

#include <stdio.h> /* dołączenie deklaracji funkcji wejścia wyjścia */ #include <math.h> /* dołączenie deklaracji funkcji sin */ int main(void) { double a,b; /* Dopóki udaje nam się przeczytać (co poznajemy po tym, że rezultat wywołania scanf wynosi 1) przetwarzamy wczytane liczby. Gdy scanf("%lf", &a) będzie różne od 1, to oznacza że nie udało się przeczytać kolejnej liczby, a wtedy program kończy działanie.*/ while (scanf("%lf", &a) == 1) /* czy kolejna warość jest wczytana?*/ { b = sin(a); /* obliczamy warto?? funcji sinus */ printf("%lf\n", b); /* wypisujemy wynik */ } return 0; }

Page 58: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

Trochę trudniejszy przykład: program który pozwoli zobaczyć, jaka jest energia dwóch naciągniętych sprężynek przyczepionych w połowie sąsiadujących boków kwadratowej ramki i połączonych drugimi końcami. Sprężynki są jednakowe i mają długość równą połowie długości boku kwadratu równą Obliczana energia wynosi � =�� = �Δ� 2⁄ , �" = �Δ# 2⁄ ,

Δ� = $(� − %) & # − %,

Δ# = $(# − %) & � − %. Założymy, że % = 5 cm, � = 1 N/cm. Chcemy wykreślić wartość siły wypadkowej w zakresie 0 ( � (obliczone z rozdzielczością co do 1 mm.

Trochę trudniejszy przykład: program który pozwoli zobaczyć, jaka jest energia dwóch naciągniętych sprężynek przyczepionych w połowie sąsiadujących boków kwadratowej ramki i połączonych drugimi końcami. Sprężynki są jednakowe i mają długość równą

ługości boku kwadratu równą %.

�� & �", gdzie

( 10 cm, 0 ( # ( 10 cm,

Page 59: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

/* Program sprężyki, wersja 0.1, © 2011 Sławomir Marczyński */ #include <stdio.h> #include <math.h> int main(void) { double k = 1.0 ; /* stała sprężystości N/m */ double a = 0.05; /* długość boku kwadratu w metrach */ double x,y; /* współrzędne w metrach */ double dx,dy; /* przyrosty długości sprężynek w metrach */ double E; /* łączna energia sprężynek w dżulach */ for (x = 0.; x <= 2*a; x += 0.001) { for (y = 0.; y <= 2*a; y += 0.001) { dx = sqrt((x-a)*(x-a) + y*y) - a; dy = sqrt((y-a)*(y-a) + x*x) - a; E = 0.5*k*dx*dx + 0.5*k*dy*dy; printf("%lf %lf %lf\n", x, y, E); } printf("\n"); } return 0; }

Page 60: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

Gdy skompilujemy ten program i potem uruchomimy, to otrzymamy nudne wyniki liczbowe.

Jednak możemy dane te zapisać do pliku i potem wykreślić np. programem gnuplot.18

Rysunek 7. Wykres energii dwóch połączonych sprężynek.

18 http://www.gnuplot.info/, 11 października 2012

Page 61: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

Podsumowanie

Poznaliśmy flow control statements jakimi w języku C możemy zapisywać algorytmy.

Do instrukcji tych należą if-else, switch, while, do-while i for oraz dodatkowo break i contiue.

Takie same instrukcje i tak samo działające,

są w językach C++, C# i Java, Java Script oraz AWK.

Page 62: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

Wykład 3

Page 63: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

Procedury i funkcje

Możliwość delegowania zadań programu do wyodrębnionych jego części jest podstawą programowania proceduralnego.

Programowania proceduralne umożliwia naturalne zapisywanie algorytmów rekurencyjnych.

Metodyka programowania programowania zstępującego (top down) zakłada, że cały problem jaki mamy rozwiązać dzielimy na podproblemy, a te podproblemy na pod-pod-problemy itd. aż otrzymamy takie zadania, które będą dostatecznie łatwe do rozwiązania. Nie należy mylić ze sobą programowania proceduralnego z programowaniem funkcyjnym (funkcjonalnym): programowanie proceduralne to paradygmat imperatywny, programowanie funkcyjne to paradygmat deklaratywny – czyli są to zupełnie różne pojęcia!

Page 64: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

W języku C funkcje19 to rodzaj części z których zbudowany jest cały program. Oczywiście, są takie funkcje jak sin(x), czyli standardowe funkcje matematyczne. Oczywiście, jeżeli w wielu miejscach w programie jest wykonywane to samo, to naturalne jest wyodrębnienie tych instrukcji w formie funkcji. Ale nawet jeżeli określone, będące sensownie ze sobą związane, instrukcje będą wykonywane tylko jeden jedyny raz, to zgrupowanie tych instrukcji w funkcji/procedurze jest dobrym pomysłem. Zasadniczym celem istnienia funkcji nie jest bowiem bardziej zwarty zapis programu, lecz rozbicie programu na niezależne od siebie części. Funkcje możemy deklarować, definiować i używać. Używanie funkcji jest najłatwiejsze – już wcześniej używaliśmy funkcji sin(x) – wystarczy napisać nazwę funkcji i jej argumenty nawiasach okrągłych, np.:

a = foo(x,y) + bar(z); /* wywoływane są funkcje foo i bar */ fum(x,y); /* wywoływana jest funkcja/procedura fum */

19 W C/C++ nie rozróżnia się procedur od funkcji, procedury uważając za funkcje zwracające void.

Page 65: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

Jeżeli kompilator nie wie, o jaką funkcję chodzi, a natrafi na jej wywołanie, to będzie usiłował rozszyfrować sam jakie wartości ona zwraca i jakie są typy jej argumentów. Czyli np. funkcję fum będzie rozumiał jako funkcję o wartościach całkowitych dwóch zmiennych całkowitych.20 A to być może nie będzie zgodne z intencją programisty. To co funkcja ma robić określamy w jej definicji, np. funkcję cube() możemy21 zdefiniować w ten sposób:

double cube(double x) { return x*x*x; }

20 Domyślnym typem argumentów jest typ int.

21 Identyfikatory naszych funkcji wybieramy tak, aby coś dla nas znaczyły. W jednym programie cube

może oznaczać trzecią potęgę liczby rzeczywistej, a w innym programie rysować na ekranie sześcian, a w jeszcze innym stop miedzi i berylu.

Page 66: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

Ogólnie składnię definicji funkcji można tak opisać

typ_funkcji nazwa_funkcji ( lista_parametrów )

{

/* … różne rzeczy … mogą też być dodatkowe instrukcje return */

return wartość_zwracana_jako_wynik ;

}

gdzie lista_parametrów to albo

typ_parametru identyfikator_parametr

albo

typ_parametru identyfikator, lista parametrów

Page 67: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

Definicja funkcji może być w innym pliku źródłowym, niż nasz program. Definicja funkcji nie musi w ogóle być dostępna dla nas jako kod źródłowy, może być skompilowana oddzielnie, może być umieszczona wewnątrz biblioteki takich oddzielnie skompilowanych funkcji.22 Może być tak, że w funcji foo() chcemy użyć funkcji bar(), a w funkcji bar() chcemy użyć funkcji foo(). Dlatego też, jeżeli same definicje funkcji nie wystarczają, to używamy deklaracji funkcji. 23 W uproszczeniu: deklaracja to definicja funkcji pozbawiona treści zawartej w nawiasach {}. Nawiasów klamrowych też nie ma. Gdy kompilator napotka deklarację, to dowie się on jakie funkcja będzie miała parametry i jakie typy będą miały te parametry.

22 Funkcje standardowe, takie jak printf, nie są kompilowane za każdym razem, tylko dołączana z

biblioteki aby tłumaczenie programu przebiegało szybciej. 23

W języku C++ definiowanie/deklarowanie funkcji przed ich użyciem w tekście programu jest obowiązkowe.

Page 68: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

Na przykład deklaracja naszej funkcji cube ma postać:

double cube(double);

Ja widać, niepotrzebne jest pisanie argumentów, a jedynie podanie ich typów.24

W praktyce jednak po prostu kopiuje się początkowy fragment definicji funkcji, włącznie z parametrami, i tylko dopisuje średnik:

double cube(double x);

W miarę potrzeby takie deklaracje gromadzi się w plikach z rozszerzeniem25 .h, które dołącza się dyrektywą #include.

24 Nie trzeba w C nawet pisać jakie mają być argumenty, ale jest to niebezpiecznie i niezalecane.

25 Pliki programów w języku C zwykle mają nazwy kończące się na .c, pliki z deklaracjami funkcji itp.

mają nazwy kończące się na .h .

Page 69: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

Uwaga 1: wcześniej był w użyciu inna składnia definicji funkcji, obecnie niezalecana i nieużywana, w której naszą funkcję cube napisalibyśmy jako:

double cube(x) double x; { return x*x*x; }

Pisząc w ten sposób nie można użyć copy-paste do szybkiego utworzenia deklaracji, dlatego w nowych programach praktycznie go się nie spotyka. Uwaga 2: zanim poznamy wskaźniki i/lub referencje nie możemy przekazywać danych z wnętrza funkcji na zewnątrz przez parametry. Dzięki wskaźnikom jest to, w języku C, możliwe i często używane, na przykład funkcja scanf("%d",&x) używa wskaźnika &x. W języku C++ znacznie wygodniej jest stosować referencje. W języku Java celowo jest to zabronione (i niemożliwe): aby zwrócić wynik musimy użyć instrukcji return.

Page 70: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

Funkcja może wywoływać samą siebie, typowym26 przykładem jest funkcja silnia jak poniżej:

double silnia(int n) { if (n > 0) return n*silnia(n-1); else return 1; }

Każde wywołanie funkcji w języku C oznacza konieczność zarezerwowania pamięci dla automatycznych zmiennych lokalnych i dla zapamiętania miejsca, do którego ma powrócić procesor gdy wykonywana będzie instrukcja return. Dlatego wywołanie funkcji silnia(50) nie powinno sprawiać kłopotów, ale funkcja f(){ return f(); } musi27 doprowadzić do kryzysu i takiej rekursji należy unikać.

26 Nietypowym jest funkcja main() wywołująca sama siebie.

27 W języku Logo tak działająca funkcja nie spowoduje wyczerpania pamięci.

Page 71: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

Za kolejny przykład posłuży nam program gaszący i zapalający żarówkę za pomocą fikcyjnej biblioteki Remote Light Bulb Control Module udostępniającej funkcję rlbcm(id, state), w której id to numer żarówki, a state to żądany stan żarówki: state = 1 gdy żarówka ma być włączona i state = 0 gdy ma być wyłączona. Nie wiemy jak wyglądają szczegóły implementacji funkcji rlbcm, po prostu otrzymaliśmy ją od producenta interfejsu inteligentnych żarówek.28 Przykład jest fikcyjny, ale wiele zupełnie prawdziwych przyrządów obsługuje się w podobny sposób – producent hardware dostarcza bibliotek softwareowych z gotowymi funkcjami.

28 Istnieją już tzw. inteligentne budynki, w których każdą żarówkę zapalać można zdalnie programem

komputerowym.

Page 72: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

Ustaw to co jest potrzebne do ustawienia na początku

Powtarzaj tyle razy ile trzeba

{

zapytaj jaką operację chce wykonać użytkownik

zrób to co kazał zrobić użytkownik

}

Zrób to co jest potrzebne do zakończenia

Oczywiście, moglibyśmy po prostu usiąść i napisać taki program linia po linii, wszystko umieszczając wewnątrz main(). Ale wygodniej jest podzielić program na funkcje/procedury.

/************************************************************************ * Program light, wersja 0.1, © 2012 Sławomir Marczyński * Program demonstrujący podział programu na procedury/funkcje. ************************************************************************/ #include <stdio.h> /* standardowa biblioteka wej?cia-wyj?cia */ #include <locale.h> /* setlocale */ #include "rlbcm.h" /* niestandardowa biblioteka (deklaracje) */

Page 73: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

static int id; /* globalny numer identyfikacyjny ?arówki */ void setup() { id = 1001; setlocale(LC_ALL,""); } void cleanup(void) { rlbcm(id,false); } int menu(void) { int n; puts("Zapalanie żarówki : wpisz 1 i naciśnij enter"); puts("Gaszenie żarówki : wpisz 0 i naciśnij enter"); puts("Zakończenie programu: wpisz -1 i naciśnij enter"); scanf("%d",&n); return n; }

Page 74: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

int main(void) { int cmd; setup(); do { cmd = menu(); switch (cmd) { case 0,1 : rlbcm(id,cmd); break; } } while (cmd != -1 ); cleanup(); return 0; }

Page 75: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

Wykład 4

Page 76: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

Literały, zmienne, stałe

Komentarz to ta część tekstu programu, która jest pomijana przy kompilacji/translacji kodu źródłowego. W językach C/C++/C#/Java komentarz pisze się po dwóch ukośnikach do końca linii, lub ujmując go w „nawiasy” ukośnik-gwiazdka i gwiazdka-ukośnik: /* komentarz */

Literałami nazywa się konkretnie zapisane w tekście, czyli kodzie źródłowym programu, dane takie jak liczby i znaki, na przykład:

3.1415972 "Ala ma komputer"

Literały z natury rzeczy są stałe29 i dlatego nie mogą przechowywać zmieniających się wyników obliczeń. Do tego służą zmienne, będące podstawowym elementem każdego języka programowania imperatywnego.

29 Chyba, że program modyfikuje swój własny kod źródłowy w trakcie wykonania przez interpreter, co

np. umożliwiała funkcja code w Spitbolu 4.

Page 77: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

Zmienna, w matematyce, to symbol zastępczy określający jeden, wybrany z określonego zbioru, element. Jeżeli zbiór ten jest jednoelementowy, to zamiast o zmiennej mówi się o stałej. Wprowadzenie pojęcia zmiennej umożliwiło powstanie algebry. Na przykład aby pokazać przemienność dodawania można napisać ogólnie % & ) = ) & % zamiast 2 & 3 = 3 & 2. W imperatywnym paradygmacie programowania, zmienna jest to symboliczne określenie miejsca przechowywania danej (określanej jako wartość zmiennej) , co zwykle wymaga pamięci komputera. Tak pojmowana zmienna, jeżeli nakażemy to odpowiednią instrukcją, może mieć zmienianą wartość i to różni ją od zmiennej matematycznej. W tekście źródłowym programu zmienna jest zapisywana w postaci identyfikatora, czyli nazwy służącej do identyfikacji. Jeżeli identyfikator określa zawsze taką samą wartość, której nie można zmienić w trakcie działania programu, to jest identyfikatorem stałej. Wszędzie tam, gdzie użyć można literałów, można też użyć zamiast nich zmiennych. Możemy też ze zmiennych, literałów, operatorów, nawiasów itd. tworzyć wyrażenia, które po ewaluacji mają pewną wartość.

Page 78: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

Typ zmiennej jest określeniem rodzaju danych jakie może zmienna przechowywać. Wszystkie możliwe wartości zmiennej danego typu tworzą – jak łatwo zauważyć – zbiór i dlatego możemy utożsamiać te dwa pojęcia. W matematyce zapis + = 1000 jest rozumiany jako stwierdzenie faktu: równości pomiędzy zmienną + i liczbą 1000. W przypadku programowania imperatywnego zapis A = 1000 rozkazuje sprawić, aby taka właśnie równość nastąpiła. Oznacza rozkaz zapisu: zapisać liczbę 1000 do pewnego miejsca w pamięci komputera, które dla wygody nazywamy A. Właśnie by odróżnić te dwa znaczenia – porównanie i przypisanie – w niektórych językach (wywodzących się od Algolu) stosuje się oznaczenie A := 1000 jako notację dla przypisania, a w innych specjalnie zapisuje się porównywanie (np. podwójnym znakiem równości w językach wywodzące się z C). Jeżeli napiszemy A = A/2 + 1, to matematyk rozwiąże równanie dając odpowiedź A = 2 oraz stwierdzi sprzeczność z wcześniej napisanym równaniem w którym było ono równe 1000. Natomiast informatyk używający języka C zrozumie: wyciągnij coś z pamięci oznaczonej przez A, podziel to przez dwa, dodaj 1, zapisz do pamięci oznaczonej przez A, przez co poprzednia zawartość tej pamięci zostanie zastąpiona nową wartością (wynoszącą 501).

Page 79: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

Każdą zmienną w języku C trzeba przed użyciem zdefiniować, czyli po prostu zarezerwować dla niej nazwę i pamięć, w której będzie przechowywana wartość zmiennej. Dobrze jest także, aby każda zmienna już na początku miała jakąś wartość, czyli była zainicjalizowana. Jeżeli będziemy chcieli użyć wartości (którą zmienna ma reprezentować) zanim ta wartość będzie ustalona, czyli użyjemy niezainicjalizowanej zmiennej, to w efekcie pojawić się mogą zupełnie niespodziewane wyniki i błędne działanie programu. Użycie zmiennej niezdefiniowanej jest wykrywane przez kompilator jako błąd. Deklaracja zmiennej nie służy do rezerwacji pamięci, a jedynie informuje kompilator o tym, że zmienna o danym identyfikatorze i danego typu jest zdefiniowana gdzie indziej. Zmienne definiujemy podając typ zmiennej, nazwę zmiennej i opcjonalnie wartość początkową, np.:

long int liczbaOwiec = 0; /* pierwsze oszacowanie */ double x; /* nieokreślona wartość początkowa */

Page 80: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

C99: zmienne można definiować tam gdzie są potrzebne (zasada minimalnej widoczności), wcześniejsze wersje standardu nakazywały to robić na początku, to jest przed innymi instrukcjami (w danym bloku ujętym w nawiasy klamrowe). Aby zdefiniować zmienne całkowite używa się typów char, short int, int, long int, long long int – albo ich odmian „bez znaku” unsigned char, unsigned short int, unsigned int, unsigned long int, unsigned long long int. Liczby bitów wynoszą dla nich odpowiednio: dokładnie 8 dla char, co najmniej 16 dla int, nie więcej dla short int niż dla int, nie mniej dla long int niż dla int, co najmniej 64 dla long long int. Zwykle będzie to 16 bitów dla short int, 32 bity dla int i dla long int , daje to zakres od −2147483648 do 2147483647 dla liczb int, ale tylko od -32768 do 32767 dla liczb short int. Słowa kluczowe short i long są traktowane jedynie jako wskazówki dla kompilatora i dlatego mogą nie mieć żadnego konkretnego działania, choć wprowadzono je po to, aby dać możliwość wyboru pomiędzy oszczędnością pamięci a dużym zakresem wartości.

Page 81: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

Sens używania liczb „bez znaku” sprowadza się do efektu „przekręcenia licznika”: jeżeli do największej liczby całkowitej „ze znakiem” 16-bitowej równej 32767 dodamy jeden, to otrzymamy -32768, czyli liczbę ujemną. Natomiast dla liczb unsigned przekroczenie zakresu o 1 da nam zero i liczby ujemne nigdy się nie pojawią. W programowaniu mikrokontrolerów używane są dodatkowo „typy z saturacją” – dla nich 32762+1 daje 32762. Przy niskopoziomowym programowaniu trzeba pamiętać o porządku bajtowym (byte order), który nie jest określony standardem C i zależy od procesora. W big-endian najpierw zapisuje się najbardziej znaczący bajt, w little-endian najpierw najbardziej znaczący. Aby umożliwić jawne określenie liczby bitów przeznaczonych na zapis liczby wprowadzono w C99 nowe typy takie jak int8_t, uint8_t, int16_t, uint16_t itd.

Page 82: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

Zmienne zmiennoprzecinkowe definiuje się jako zmienne o typie float lub double, czyli pojedynczej lub podwójnej precyzji. Zmiennych typu float nie należy używać, bo obliczenia na nich są bardzo niedokładne, a ewentualna oszczędność pamięci jest bez sensu jeżeli wyniki i/lub działanie programu będą błędne. Norma IEEE-754-1985 określa liczby pojedynczej precyzji jako 32-bitowe, mające 1 bit znaku, 8 bitów wykładnika i 23 bity mantysy, natomiast liczby podwójnej precyzji jako 64-bitowe, 1 bit znaku, 11 bitów wykładnika i 52 bity mantysy. Oznacza to, że liczby float mają tylko około 7 cyfr znaczących po konwersji na zapis dziesiętnym, natomiast liczby double około 16 cyfr. Standard C99 wprowadza także zmiennoprzecinkowe liczby zespolone, których można używać dołączywszy complex.h: symbol I oznacza jednostkę urojoną, liczby zespolone definiuje się podając dodatkowo słowo complex, np.: double complex z = 1.0 + I*0.5.

Page 83: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

W kodzie źródłowym C/C++ literały liczbowe zapisuje się z kropką jako przecinkiem dziesiętnym, np.: 3.1415927. Zapis 3E+8 oznacza 3 ⋅ 10-, a zapis 0.05E-22 to 0.05 ⋅ 10/ . Tak zapisane liczby domyślnie są podwójnej precyzji. Można, jeżeli to potrzebne, bezpośrednio po liczby zmiennoprzecinkowej dopisać literę L (albo F), aby określić, że liczba taka jest jeszcze większej precyzji niż podwójnej (albo że jest pojedynczej precyzji), np. 1.0L, 0.3F. Jeżeli w zapisie liczby nie będzie kropki ani E, to literał taki liczbą całkowitą. Literały liczbowe reprezentujące liczby całkowite to: ciągi cyfr, np. 13; ciągi cyfr zaczynające się zerem, np. 0707 (oznaczające liczby w zapisie ósemkowym); ciągi zaczynające się od 0x, np. 0xFF, oznaczające liczby szesnastkowe. Dopisanie U oraz L, LL, i64 określa odpowiednio że jest to liczba bez znaku, długa (long), podwójne długa (long long), 64-bitowa.30

30 Oznaczenia te możemy pisać małymi lub wielkimi literami.

Page 84: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

Zmienne reprezentujące znaki są typu wchar_t dla UCS (określanego w żargonie C jako wide chars) i char dla kodowania 8-bitowego znaków (takiego jak ASCII, ISO-8859-2).31 Dla kodowania UTF-8 (wielobajtowego) także można używać typu char - oczywiście jeden znak UTF-8 będzie wymagał wielu „charsów”; inną możliwością jest zapis znaków UTF-8 w 32-bitowych zmiennych typu int. Nawet jednobajtowe znaki można przechowywać w zmiennych typu int. Napisy, czyli ciągi znaków, są przechowywane w tablicach, o elementach typu char, z ostatnim znakiem o kodzie 0, oznaczanym tradycyjnie '\0' (null terminated strings).

31 char32_t dla UTF-32, char16_t dla UTF-16 są w C++0x.

Page 85: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

Literały znakowe pisze się w apostrofach, np. 'A' oznacza znak litery A. Jeżeli używamy kodowania wielobajtowego, to pomiędzy apostrofami może być do czterech bajtów, teoretycznie więc zapis 'AB' jest zapisem jednego znaku. Znaki Unicode32 zapisujemy używając prefiksu L, czyli L'c' dla litery c, a w razie potrzeby L'unnnn', gdzie nnnn jest cztero- lub ośmio- cyfrową liczbą szesnastkową.33 Stałe znakowe 8-bitowe możemy zapisywać też podając ich numeryczne kody oktalnie lub szesnastkowo, np.: '\101', '\x41'. Niektóre znaki kontrolne mają swoje uproszczone skróty, najważniejsze34 z nich to znak przejścia do nowej linii '\n'; znak tabulacji to '\t'; znak oznaczający koniec tekstu to '\0'; apostrof '\''; cudzysłów '\"'. Pojedynczy znak \ zapisuje się jako '\\', podobnie '\?' oznacza po prostu znak zapytania.35

32 http://www.utf8-chartable.de/unicode-utf8-table.pl

33 Taki zapis wprowadza C99.

34 Inne to '\a', '\r', '\v', '\f'.

35 W języku C ??< oznacza nawias klamrowy {, ??( nawias prostokątny [, ??= znak #. Jest to

rozwiązanie problemu systemów, w których nie można napisać normalnie nawiasów klamrowych itp. znaków. Aby napisać dwa znaki zapytania w tekście można użyć sekwencji ?\? .

Page 86: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

Literały napisowe zapisuje się w cudzysłowach, np. "napis ćwiczebny". Przy kodowaniu UTF/UCS możemy w pliku źródłowym pisać z polskimi literkami i nawet nie zauważymy, że łańcuch będzie miał więcej bajtów niż liter. Możemy też wpisywać kolejne bajty znaku jeden po drugim, np. "\xce\x94\xce\x86" jest napisem "Δφ".36 Jeżeli chcemy zmusić kompilator do zakodowania łańcucha w UCS, to umieszczamy L przed otwierającym cudzysłowem, np. L"\u0394\u03c6".

36 Jednak aby taki napis zobaczyć, musimy mieć odpowiednio skonfigurowany system, czcionki itd.

Page 87: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

Typ bool służy do definiowania zmiennych logicznych, tj. zmiennych mogących przyjmować wartości prawda lub fałsz (true i false). Do tego celu można też używać zmiennych innych typów: wartość zero oznacza fałsz, prawda jest oznaczana przez dowolną wartość niezerową; liczbowa wartość wyrażenia które jest prawdziwe, np. 0 == 0, jest równa 1, wartość wyrażenia fałszywego jest równa 0. Stosowanie typu bool oraz stałych true i false jest zalecane37. W C++ bool jest słowem kluczowym. Jeżeli chcemy używać typu bool w C, to musimy użyć dołączyć <stdbool.h> do naszego programu.

37 Starsze wersje C nie miały bool, stąd większość programów nie używa bool lecz int.

Page 88: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

Typ void, czyli typ pusty, nie służy do definiowania zmiennych. Zmienne typu void są z natury rzeczy nieistniejące.38 Funkcja, która zwraca void to funkcja zwracająca nic. Jeżeli zadeklarujemy funkcję z argumentem void, to oznacza, że funkcja ma parametr nic.39 Język C udostępnia możliwość definiowania własnych typów słowem kluczowym typedef,40 którego umieszczenie na początku definicji zmiennej przekształca ją w definicję typu, np.:

typedef unsigned char BYTE; /* definicja nowego typu o nazwie BYTE */

definiuje nowy typ BYTE, który jest po prostu liczbą ośmiobitową bez znaku.

38 Istnieją natomiast wskaźniki do zmiennych tego typu, czyli void* .

39 W języku C++ brak argumentów w deklaracji funkcji oznacza brak argumentów; w języku C brak

argumentów oznacza się przez void, natomiast brak argumentów to argumenty nieokreślone. 40

W języku C++ typedef jest niepotrzebny do definiowania nowych nazw typów, jeżeli typy te są klasami.

Page 89: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

Klasy pamięci41 oznaczały pierwotnie, gdzie dana zmienna jest przechowywana i jaki jest jej czas życia: static trwają przez cały program, const nie wolno zmieniać, register są trzymane w rejestrach procesora, auto na stosie,42 extern gdzie indziej, a volatile nie wiadomo jak. Modyfikatory te w różny sposób działają na zmienne lokalne i zmienne globalne. Zmienne mają zasięg: definicje ich rozciągają się aż do nawiasu klamrowego zamykającego blok43 w którym zostały zdefiniowane. Zmienne o tej samej nazwie, ale zdefiniowane na różnym poziome zagnieżdżenia nawiasów klamrowych, wzajemnie się przesłaniają (shadowing). Jeżeli zmienne zostały zdefiniowane na zewnątrz wszystkich bloków, to zmienne takie są zmiennymi globalnymi. Zmienne, które nie są globalne są lokalne.

41 Klasa pamięci nie ma nic wspólnego z pojęciem klasy obiektów, które jest jednym z fundamentów

C++. 42

Pojęcie stosu wyjaśnione zostanie przy opisie procedur. 43

Blokiem nazywamy część tekstu źródłowego programu objęta parą odpowiadających sobie nawiasów klamrowych { }; w językach wywodzących się z Algolu zamiast nawiasów używane są słów begin i end.

Page 90: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

Zmienna która ma klasę pamięci const nie jest zmienną, lecz stałą – czyli ma mieć wartość niezmienną w czasie swojego istnienia. Wartość tą nadajemy w czasie definicji takiej zmiennej – później jest to już niemożliwe, bo kompilator wymusza, aby stałe były stałe i nie zmieniane po definicji. Zmienna volatile może zmieniać się nie tylko w efekcie działania programu, ale „sama z siebie”, np. może być modyfikowana przez inny program, czy wręcz inny hardware niż komputer. Tego rodzaju zmienne mogą np. reprezentować przetworniki układów pomiarowych, albo wartości modyfikowane przez drugi rdzeń procesora. Słowo kluczowe auto oznacza w C zmienne automatyczne. W języku C++ słowo auto, zgodnie z nowymi standardami, stało się przydatne pozwalając definiować zmienne bez podawania jawnie ich typu. Register w definicji zmiennej jest wskazówką dla kompilatora do trzymania jej w rejestrze procesora. Zmienne register mogą nie mieć adresu, bo nie są w RAM komputera. Zmienne statyczne istnieją cały czas, inicjalizacja zmiennych statycznych jest przeprowadzana jednokrotnie w programie. Jeżeli nie jest podana wartość inicjująca, to zmienne statyczne są wypełniane zerami. Zmienne automatyczne są inicjalizowane wielokrotnie!

Page 91: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie
Page 92: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

int globalnaZmienna = 1984; static int globalnaZmiennaStatyczna = 0; extern int globalnaZmiennaEx; volatile int zmiennaUlotna; enum { jeden = 1, dwa, mnostwo } ile; int main(void) { const int zmiennaLokalna = 0; static int statycznaZmiennaLokalna = -1; { auto int zmiennaAutomatyczna = 88; register int zmiennaLokalna = 1; extern int innaZmiennaZewnetrzna; printf("%d\n", zmiennaLokalna+innaZmiennaZewnetrzna); } printf("%d\n", zmiennaLokalna); /* wydrukuje 0 */ return statycznaZmiennaLokalna + 1; }

Page 93: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

Nazwa Przykład Znaczenie Modyfikatory

int int liczbaOwiec = 0; liczba całkowita signed, unsigned, short, long, long long

double double q = 1.6E-19; liczba zmiennoprzecinkowa

long

float const float g = 9.81; liczba zmiennoprzecinkowa

char static char c = 'A'; znak signed, unsigned

wchar_t wchar_t = L'Ą'; znak

bool bool prawda = (2 + 2 == 4); zmienna logiczna

void void* ptr; nieokreślony rezultat

enum enum { jeden, dwa, mnostwo } ile;

zmienne wyliczeniowe

Page 94: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

Modyfikator Znaczenie

signed ze znakiem

unsigned bez znaku

short krótka postać (oszczędzająca pamięć, np. dwubajtowa)

long długa postać (dla większych liczb, np. czterobajtowa)

long long bardzo długa postać

Page 95: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

Klasa pamięci Znaczenie Uwagi

const stałe, niekoniecznie reprezentowane przez zapis w RAM

mają tylko jedną ustaloną wartość

static zmienne statyczne, nie są widoczne poza danym modułem

istnieją nawet wtedy, kiedy są niewidoczne

auto zmienne automatyczne istnieją tylko wtedy, gdy są widoczne

extern zmienne zewnętrzne zwykle są to zmienne globalne

volatile zmienne ulotne, prawo do zapisu ma nie tylko (jeden) procesor

zmienne, mogące zmieniać swoją wartość niezależnie od programu

register w rejestrach CPU (o ile to możliwe) szybki dostęp, nie w RAM

Page 96: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

Praktycznie do prostu zacząć od używania zwykłych liczb int i double, pozostałe typy zmiennych będą przydatne znacznie rzadziej.

Page 97: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

Kodowanie, gęsi i inne polskie problemy

Komputery zapisują teksty jako ciągi bitów. Obecnie używa się zestawu znaków Unicode, pozwalającego na zapisywanie tekstów w każdym używanym alfabecie języków naturalnych. Znaki Unicode są kodowane jako44 UTF-32, UTF-16, UTF-8, UCS-2, UCS-4 i na kilka jeszcze sposobów, każdy jednak zakłada, że jeden znak może być kodowany w wielu bajtach: w przypadku UTF-32 są to zawsze cztery bajty; UTF-16 niektóre znaki zapisuje dwubajtowo, niektóre czterobajtowo; UTF-8 potrzebuje od jednego do czterech45 bajtów na jeden znak. Dawne standardy, takie jak pięciobitowy ITA146 i ITA2 czy ośmiobitowy EBCDIC47 praktycznie są już nieużywane. Siedmiobitowy ASCII48 i ośmiobitowe rozszerzenie ASCII w postaci ISO/IEC-8859, w tym interesujące nas ISO/IEC-8859-2 (Latin-2) zgodne z PN-93 T-42118 (zawierające litery polskiego alfabetu), pozwalały na utożsamianie znaków z bajtami.

44 Unicode Transformation Format, Universal Character Set

45 Pierwotna propozycja zakładała 6 bajtów

46 Kod Baudota International Telegraph Alphabet No. 1

47 Extended Binary Coded Decimal Interchange Code

48 American Standard Code for Information Interchange

Page 98: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

Dlatego język C, podobnie jak dawniej stosowane systemy operacyjne,49 do reprezentowania pojedynczych liter używał jednobajtowego typu char. Typem obsługującym kodowanie UCS jest, zamiast char, typ „szerokich znaków” wchar_t określony w wchar.h. Znaki UTF są w C traktowane jako „znaki wielobajtowe”, czyli po prostu sekwencje znaków char. Najważniejsze, co musimy zapamiętać, to fakt, że jeden znak może zajmować w pamięci kilka50 bajtów, dlatego aby przechować 256 znaków potrzeba około kilobajta pamięci. Programy do obliczeń technicznych zwykle używają do komunikacji z użytkownikiem języka angielskiego (odmiany używanej w USA). Niemniej jednak jeżeli program ma napisać cytat „Grzegorz Brzęczyszczykiewicz, Chrząszczyżewoszyce powiat Łękołody”, to musi jakoś zakodować polskie literki. Żywiołowe przystosowywanie kodu ASCII do tego celu dało np. kodowanie księdza Jana Pikula xJP, AmigaPL, Mazovia, IEA-Świerk, Elwro-Junior, CSK, DHN, DOS Latin-2 (CP852), Microsoft Windows CP-1250 i inne. Oczywiście, pisząc program w języku C można było użyć printf("łopata").

49 Teraz systemy operacyjne używają wewnętrznie UTF-16 (Microsoft Windows) lub UTF-8 (Linux).

50 Maksymalna liczba bajtów jest określona przez stałą MB_LEN_MAX zdefiniowaną w limits.h .

Page 99: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

Problemy zaczynały się wtedy, gdy na przykład:

a. gotowy program był uruchamiany np. w USA na komputerze „rozumiejącym” wyłącznie zwykłe ASCII;

b. kod źródłowy programu był edytowany w Izraelu (np. CP862, ale tam też powstało

ponad pół tuzina doraźnych rozwiązań, jeszcze drobiazg – tekst biegnie od prawej do lewej);

c. napis miał być przetłumaczony na chiński.

Mniej drastycznym przykładem jest stosowanie greckich liter (np. skrótu μm) i symboli matematycznych.

Page 100: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

Obecnie kompilatory bezproblemowo czytają kod źródłowy zapisany znakami Unicode z kodowaniem UTF. Jeżeli ktoś chce, może używać w programach nazw zmiennych takich jak ŚredniaGłębokość. Można używać printf("łopata - лопата"), czyli używać PL-literek w napisach, jakie mają być wyprowadzane przez program. Oczywiście PL-literki mogą pojawiać się w komentarzach. Ale ma to swoją cenę:

- konieczne jest prawidłowe skonfigurowanie systemu; - jeżeli używany jest Unicode to jeden znak to może być wiele bajtów; - programy z tekstami w języku polskim będą wymagały znajomości języka polskiego

od użytkowników. Można nadal stosować jednobajtowe kodowanie ISO/IEC-8859-2, lecz wtedy pojawią się trudności z jednoczesnym użyciem np. alfabetu łacińskiego i cyrylicy.

Page 101: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

Wybór, jakiego kodowania napisany program ma używać gdy zostanie uruchomiony, jest możliwy przez wywołanie funkcji setlocale(), np.:

setlocale(LC_ALL,"C") jest domyślnym ustawieniem gwarantującym „tradycyjne zachowanie” (a tym samym kropkę zamiast przecinka dziesiętnego i problemy z PL-literkami);

setlocale(LC_ALL,"") przejmuje ustawienia systemowe; setlocale(LC_ALL,"pl-PL.ISO-8859-2") powinno51 ustawić język polski, przecinek

dziesiętny, polskie reguły pisania dat i waluty oraz kodowanie jednobajtowe napisów.

51 Jeżeli jest to możliwe, co systemach uniksowych sprawdzić to można komendą locale -a .

Page 102: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

1 /************************************************************************ 2 * Program gesi, wersja 0.1, © 2011 Sławomir Marczyński 3 * 4 * Program który testuje wyprowadzanie liczb i tekstu w języku polskim. 5 ************************************************************************/ 6 7 #include <stdio.h> 8 #include <locale.h> 9 10 #define NUMBER 3.14 11 #define TEXT " Chrząszczyżewoszyce" 12 13 void test(char* loc1, char* loc2) 14 { 15 setlocale(LC_ALL,"C"); /* resetowanie */ 16 setlocale(LC_ALL ,loc1); /* wszystkie opcje na raz */ 17 setlocale(LC_NUMERIC,loc2); /* tylko sposób wyprowadzania liczb */ 18 printf("%f %s\n", NUMBER, TEXT); /* wypisywanie liczby i tekstu */ 19 } 20 21 int main(void) 22 { 23 test("C","C"); 24 test( "", ""); 25 test( "","C");

Page 103: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

26 27 printf("\ndla systemu Microsoft Windows\n\n"); 28 29 test("polish_poland.1250", "polish_poland.1250"); 30 test("polish_poland.1250", "C" ); 31 32 printf("\ndla systemu Linux\n\n"); 33 34 test("pl_PL.ISO-8859-2", "pl-PL.ISO-8859-2"); 35 test("pl_PL.ISO-8859-2", "C" ); 36 test("pl_PL.UTF-8", "pl-PL.UTF-8" ); 37 test("pl_PL.UTF-8", "C" ); 38 } 39

Page 104: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

Wykład 5

Page 105: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

Operatory

Pisząc wyrażenie a = b + c używamy identyfikatorów trzech zmiennych połączonych operatorami. Kompletna lista operatorów jest dość długa, dodatkowo w języku C++ operatory możemy redefiniować.52 Na szczęście nie trzeba wszystkich operatorów uczyć się na pamięć jednego dnia. Najbardziej oczywiste i potrzebne są operatory +, -, *, / jako oznaczenie dodawania, odejmowania, mnożenia i dzielenia.53

52 Hierarchia operatorów w C++, czyli kolejność wykonywania działań, jest ustalona i nie zmienia się

nigdy. 53

Dlatego zostały ocenione na pięć gwiazdek.

Page 106: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

Nazwa Znaczenie C Przykład Przydatność

:: zakres ważności nazwy a::b ****

. określenie pola struktury, unii lub klasy � a.b ****

-> określenie pola obiektu wskazywanego � a->b ****

[] określenie elementu tablicy � a[i] *****

() nawiasy w różnych zastosowaniach � (a + b) *****

sizeof określenie rozmiaru obiektu lub typu � sizeof (a) ****

++ post-inkrementacja � a++ ***

++ pre-inkrementacja � ++a ***

-- post-dekrementacja � a-- ***

-- pre-dekrementacja � --a ***

~ negacja bitów � ~a **

! negacja logiczna � !a ***

- zmiana znaku � -a *****

+ zachowanie znaku � +a *

& określenie adresu � &a ****

* zawartość pod adresem � *a *****

new kreacja new a ****

delete anihilacja delete a ****

new[] kreacja tablicy new a[n] **

delete[] anihilacja tablicy delete[] a; **

(typ) rzutowanie na typ � (int)a ****

Page 107: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

.* dereferencja a.*b *

.-> dereferencja wskaźnikiem a->*s *

* mnożenie � a*b *****

/ dzielenie � a/b *****

% reszta z dzielenia � a%b ***

+ dodawanie � a+b *****

- odejmowanie � a-b *****

<< przesunięcie w lewo/wstawiania do strumienia �/� a << b **/*****

>> przesunięcie w prawo/wyciągania ze strumienia �/� a >> b **/*****

< porównanie, mniejsze � a < b *****

> porównanie, większe � a > b *****

<= porównanie, niewiększe � a <= b *****

>= porównanie, niemniejsze � a >= b *****

== porównanie, równe � a == b *****

!= porównanie, nierówne � a!=b *****

& iloczyn bitów � a&b **

^ różnica symetryczna bitów � a^b **

| suma bitów � a|b **

&& koniunkcja � a&&b ****

|| alternatywa � a||b ****

? warunkowy wybór � a ? b : c ***

= przypisanie wartości � a = b *****

Page 108: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

*= mnożenie i przypisanie � a*=b ***

/= dzielenie i przypisanie � a/=b ***

%= reszta i przypisanie � a%=b **

+= dodawanie i przypisanie � a+=b ***

-= odejmowanie i przypisanie � a-=b ***

<<= przesunięcie w lewo i przypisanie � a<<=b **

>>= przesunięcie w prawo i przypisanie � a>>=b

&= iloczyn bitów i przypisanie � a<<=b **

|= suma bitów i przypisanie � a>>=b **

^= różnica symetryczna bitów i przypisanie � a<<=b **

, przecinek rozdzielający wyrażenia � a,b ***

Page 109: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

Jeszcze ważniejszy jest operator przypisania wartości, czyli polecenie nakazujące zmianę wartości reprezentowanej w zmiennej54 po jego lewej stronie (l-value) na wartość wyrażenia z prawej strony (r-value):

lhs = rhs;

Ponieważ wynik operacji przypisania jest wartością rhs po przypisaniu, to można np. używać przypisania wielokrotnie:

a = b = c = d = 2048;

Języki C/C++/C#/Java kontynuują tradycję Fortranu – przypisanie jest zapisywane po prostu przez znak = . Jednak znak ten trzeba rozumieć jako „wylicz i podstaw”, w odróżnieniu od operatora porównania „to jest tyle samo”: nic nie jest porównywane, natomiast poprzednia wartość zapamiętana w zmiennej lhs ulega „zamazaniu” przez nową wartość wyliczoną przez rhs.

54 Lub wyrażeniu, którego wynikiem jest nie wartość, lecz właśnie zmienna.

Page 110: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

Do porównywania służy operator porównania ==, na przykład a == b . W czasie porównywania nic nie jest „zamazywane”, wynikiem porównania jest prawda lub fałsz (true/false), dawniej po prostu nie-zero lub zero. Dla ułatwienia pisanie programów wprowadzono operatory takie jak += oraz ++. Ponieważ procesory miały instrukcję „zwiększ o jeden”, to twórcy C uznali, że byłoby rozsądne zamiast zapisywać i = i + 1 pisać i++. Podobnie zapis i-- zmniejsza o jeden. W języku C/C++ jest ważne, czy napiszemy i++ czy ++i. W pierwszym przypadku wartość i będzie powiększona „po” (tzn. wartość wyrażenia i++ wynosi i), w drugim wartość i będzie powiększona „przed” (tzn. wartość wyrażenia ++i wynosi i+1). Jak widać nazwa C++ to po prostu C powiększone o jeden. Jeżeli ktoś uważa że to nieco zagmatwane… nie powinien się martwić – używanie tych wszystkich operatorów, i++ zamiast i = i + 1 itp. nie jest obowiązkowe. Podobnie jak stosowanie operatorów zbudowanych według schematu x op= y, gdzie op to jeden z operatorów dwuargumentowych +-/*^&|<<>>, natomiast x, y to wyrażenia, czyli x = x op y.

Page 111: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

Na przykład x += y to to samo co x = x + y, co daje zysk gdy np. mamy napisać:

a[i+j].b[f(m)].c = a[i+j].b[f(m)].c + 2 ;

piszemy nieco krócej i nieco przejrzyściej

a[i+j].b[f(m)].c += 2 ;

Page 112: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

/************************************************************************ * Program operatory, wersja 0.1, © 2011 Sławomir Marczyński * * Program demonstrujący różne operatory wbudowane w język C. * * Pominięte zostały operatory & i * związane z manipulacją na wskaźnikach * i operatory rzutowania typów. ************************************************************************/ int main(void) { int i,j,k; /* * Operatory przypisania. */ i = 1; /* i przyjmuje wartość 1 */ i = i + 1; /* i przyjmuje wartość i + 1 */ i += 1; /* i przyjmuje wartość i + 1 (jest to zapis skrócony) */ i -= 1; /* i przyjmuje wartość i - 1 (jest to zapis skrócony) */ i *= 2; /* i jest mnożone przez 2 */ i /= 2; /* i jest dzielone przez 2 */ /* Zapis poniżej jest obecnie przestarzały - może być */

Page 113: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

/* przyczyną błędów, bo ma być odczytany jako i = i + 1, */ i =+ 1; /* ale jest zbyt podobny do i = (+1) */ /* * Operatory inkrementacji i dekrementacji - przed i po wykorzystaniu * wyniku ich działania. */ ++i; /* jest to zapis równoważny instrukcji i = i + 1; */ k = ++j; /* inaczej para instrukcji j = j + 1; k = j; */ k = j++; /* inaczej para instrukcji k = j; j = j + 1; */ --i; /* jest to zapis równowa?ny instrukcji i = i - 1; */ k = --j; /* inaczej para instrukcji j = j - 1; k = j; */ k = j--; /* inaczej para instrukcji k = j; j = j - 1; */ /* * Operator trójargumentowy. Jeżeli i jest równe zero (porównanie * zapisuje się w języku C pisząc dwa znaki równości obok siebie), * to j będzie równe 1, jeżeli i jest różne od zera to j jest równe 2 */ j = (i == 0) ? 1 : 2 ; /*

Page 114: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

* Operatory działające nie na liczbach, ale na bitach, będących * obrazem zmiennych w rejestrach i pamięci komputera. Są one bardzo * ważne przy tworzeniu niskopoziomowego oprogramowania, sterowników * itp. C w tych zastosowaniach wypiera asemblery. */ i << 1; /* przesunięcie w lewo (mnożenie przez dwa). */ i >> 1; /* przesunięcie w prawo (dzielenie przez dwa). */ i | j; /* alternatywa (OR) */ i & j; /* koniunkcja (AND) */ i ^ j; /* wykluczanie (EXOR) */ ~i; /* negacja bitów */ i = i & 1; /* wyodrębnienie najmniej znaczącego bitu */ i = i | 1; /* ustawienie najmniej znaczącego bitu */ i = i & ~1; /* zerowanie najmniej znaczącego bitu */ /* * Operatory porównania (znak nierówności może być przeciwny) */ i == j; // równe i != j; // nierówne i > j; // większe i >= j; // niemniejsze

Page 115: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

/* * Operatory logiczne, działające na zmiennych reprezentujących * true/false. */ !i; /* negacja logiczna. */ i && j; /* logiczna koniunkcja (jest prawdą i oraz j). */ i || j; /* logiczna alternatywa (przynajmniej jedno prawdziwe). */ k = (i > 0) && (j > 0) || !k; // przykład wyrażenia logicznego return 0; }

Page 116: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

Wykład 6

Page 117: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

Tablice, struktury, unie i enum

Tablica, w języku C, to fragment pamięci komputera, w której poukładane są samego typu elementy. Struktury to fragment pamięci komputera, w którym poukładane są kolejno elementy. Unia to fragment pamięci komputera, w którym poukładane są różnego typu elementy.

, w języku C, to fragment pamięci komputera, w której poukładane są kolejno takiego

to fragment pamięci komputera, w którym poukładane są kolejno różnego typu

to fragment pamięci komputera, w którym poukładane są w tym samym miejscu

Page 118: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

W obliczeniach matematycznych powszechnym zastosowaniem tablic jest przechowywanie wektorów i macierzy. Napisy, jako łańcuchy znaków, to także tablice. Do deklaracji tablic i odwołaniania się do elementów zapisanych w tablicach służą w językach C/C++/C#/Java nawiasy kwadratowe. W języku C zmienna o nazwie tablicy jest tym samym co wskaźnik na początek pamięci w której tablica jest przechowywana. Tablice możemy inicjalizować podając listę wartości ich kolejnych elementów oddzielonych przecinkami ujętą w nawiasy klamrowe. Dla tablic-łańcuchów znaków55 można zamiast kolejnych liter podawać literał napisowy, zostanie on automatycznie zakończony znakiem '\0'.

55 Typu char lub wchar_t .

Page 119: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

int u[100]; ... u[1] = 1; u[2] = 2; ... if ( u[2] > u[1] + 1 ) { ... }

double dane[2][100]; double x[100], y[100]; ... x[0] = dane[0][0]; y[0] = dane[1][0]; x[1] = dane[0][1]; y[1] = dane[1][1]; x[2] = dane[0][2]; y[2] = dane[1][2]; ... s3 = y[1] + y[2] + y[3];

Page 120: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

Możemy opuścić rozmiar tablicy, jeżeli wyniknie on z inicjalizacji. Ponadto C99 dodaje nową, łatwą, możliwość inicjalizacji tylko wybranych elementów:

int a[5] = { 1, 2, 3, 4, 5 }; int b[] = { 5, 4, 3, 2, 1 }; char c[20] = {'p','i','c','1','.','j','p','e','g', '\0'}; char d[20] = "pic2.jpeg"; char e[] = "pic3.jpeg"; int f[5] = {[0] = 1, [4] = 2};

Page 121: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

Efektywnym środkiem do przetwarzania danych w tablicach jest pętla for, pozwala ona powtórzyć obliczenia kolejno na wszystkich elementach tablicy.

double dane[2][100]; double x[100], y[100]; ... for (i = 0; i < 100; i++) { x[i] = dane[1][i]; y[i] = dane[2][i]; }

Istnieją także komputery wektorowe SIMD56, w których wszystkie elementy tablic mogą być przetwarzane równocześnie. Takie możliwości dla komputerów klasy PC dają instrukcje SSE57 wprowadzone przez firmę Intel i instrukcje 3DNow! firmy AMD, oraz technologia CUDA58 firmy Nvidia.

56 Single Instruction, Multiple Data

57 Streaming SIMD Extension

58 Compute Unified Device Architecture

Page 122: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

Jednym z charakterystycznych problemów związanych z tablicami było określenie jaką mają mieć wielkość: tablica musiała być zadeklarowana przez podanie, jako jej rozmiaru, stałej możliwej do określenia w czasie kompilacji. Zbyt duże tablice niepotrzebnie zajmują pamięć, która może być potrzebna na coś innego. Zbyt małe nie pomieszczą wszystkich danych. Standard C99 wprowadza VLA59, czyli tablice o rozmiarze określanym w czasie wykonania programu.

59 Variable Length Arrays

Page 123: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

int vla_demo(int liczba_elementow) { int i; int s; int a[liczba_elementow]; for (i = 0; i < liczba_elementow; i++) { a[i] = i; } for (i = 0; i < liczba_elementow; i++) { s = a[i]; } return s; }

Page 124: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

Teoretycznie, według standardu C99, powinno się udać przydzielić tablicę zajmującą do 65535 bajtów… jeżeli jest dostateczna ilość wolnej pamięci w postaci takiego ciągłego bloku. W praktyce można mieć tablice znacznie większe, lecz może się też zdarzyć, że po prostu komputer nie będzie miał wolnej pamięci w jednym kawałku o pożądanej wielkości. Tablice VLA są umieszczane na stosie, dlatego tym bardziej jest trudne zagwarantowanie, że tam się zmieszczą. Tablicami nie można posługiwać się bezmyślnie, bo programy napisane w języku C nie sprawdzają, czy nie został przekroczony zakres indeksów do tablic.60

60 Możliwe jest, że system operacyjny wykryje poważne naruszenia dostępu do pamięci. Ale sam język

C nie ma mechanizmów kontrolnych.

Page 125: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

int vla_demo_bug(int liczba_elementow) { int i; int s; int a[liczba_elementow]; for (i = 0; i < liczba_elementow; i++) { a[i] = i; } for (i = 0; i < liczba_elementow + 10; i++) { s = a[i]; } for (i = 0; i < liczba_elementow + 10; i++) { a[i] = a[i] - s / liczba_elementow; } return s; }

Page 126: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

Porównywanie tablic nie porównuje zawartości, podobnie przypisanie a = b nie kopiuje zawartości tablic. Jest tak dlatego, że nazwa tablicy (bez nawiasów kwadratowych) to po prostu wskaźnik na początek tablicy.

int a[3] = { 1, 2, 3 }; int b[3] = { 1, 2, 3 }; int c[3]; if (a == b) // źle, zawsze będzie wynik false c = a; // źle, tablica nie będzie skopiowana! else c = b; // źle, tablica nie będzie skopiowana!

Page 127: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

Tablice przekazywane są jako argumenty do parametrów funkcji właśnie jako wskaźniki. Nie ma potrzeby podawania rozmiaru tablic-parametrów, wystarczy że jest para nawiasów []. Dlatego wartości elementów tablic zmieniane wewnątrz funkcji będą zmienione także na zewnątrz funkcji. Przykładowo procedura zamiana dwóch macierzy o rozmiarze n na m, mogłaby wyglądać np. tak:

void swapmatrix(int n, int m, double a[][], double b[][]) { int i,j; double c; for (i = 0; i < n; i++) for (j = 0; j < m; j++) { c = a[i][j]; a[i][j] = b[i][j]; b[i][j] = c; } }

Page 128: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

Struktury, podobnie jak tablice, są złożonymi typami danych. Struktury pozwalają na opatrywanie wspólnym identyfikatorem danych różnego rodzaju, czyli także na łączenie prostych zmiennych, wskaźników do zmiennych i wskaźników do funkcji, które ogólnie określa się jako pola struktury. Składnia definiowania znaczników struktur jest niezbyt skomplikowana, na przykład moglibyśmy zdefiniować znacznik61 struktury mogącej przechowywać liczbę całkowitą obok łańcucha znaków:

struct foo { int n; char s[256]; };

61 Znaczniki struktur, w języku C, tworzą odrębną przestrzeń nazw.

Page 129: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

Alternatywnie zamiast znaczników struktur można użyć typedef do zdefiniowania typu struktury:

typedef struct { double x; double y; } bar;

Jeżeli używamy znaczników struktur, to dla zdefiniowania/zadeklarowania zmiennej strukturalnej musimy, w języku C, pisać słowo struct.62 Nie robimy tego, gdy zmienne definiujemy/deklarujemy używając typu strukturalnego.

62 W języku C++ można pominąć słowo struct.

Page 130: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

Zmienne strukturalne możemy inicjalizować, C99 daje także możliwość inicjalizowania wyłącznie wybranych pól.

struct foo s1; struct foo s2 = {7, "James Bond"}; struct foo s3 = {n = 8}; static foo s4; /* wszystkie pola struktury wypełnione są zerami */ bar b1, b2; bar b3 = { 15.0, 10.0 }; static b4; /* wszystkie pola struktury wypełnione są zerami */

Struktury można kopiować przez podstawienie (ale nie można w łatwy sposób porównywać), struktury mogą być parametrami funkcji, struktury mogą być wartościami zwracanymi przez funkcje. Dostęp do pól struktury realizuje się operatorem wyboru pola struktury zapisywanym jako kropka. Można mieć wskaźnik do struktury, strukturę zawierającą wskaźniki (także do samej siebie), można określić wskazanie na (czyli adres) pól struktury.

Page 131: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

struct foo buz(struct foo a) { a.n = 2 * a.n + 1; return a; }

Ponieważ bardzo często używane są wskaźniki do struktur zamiast samych struktur to, dla ułatwienia, mając wskaźnik ptr do struktury mającej pole field, zamiast pisać (*ptr).field pisze się ptr->field. W ten sposób można zapisać np.

(*(*(*ptr).f1).f2).f3).f4

jako znacznie czytelniejsze

ptr->f1->f2->f3->f4 .

Page 132: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

Struktury są bardzo przydatne nawet w programach pisanych w C, ale to w C++ są fundamentem programowania obiektowego jako klasy. Nowością w języku C są literały złożone (C99) pozwalają na łatwe konstruowanie literałów będących strukturami bez tworzenia dodatkowo zmiennych, np.:

buz( (struct foo) { 6,"Alec Trevelyan" } );

Page 133: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

Unie są definiowane/deklarowane/używane zupełnie tak samo jak struktury. Jednak ich przeznaczenie jest odmienne: pozwalają na utworzenie zmiennej reprezentującej więcej niż jeden typ. Przykładowo, jeżeli chcielibyśmy zobaczyć jakie bajty są zapisane w miejscu w którym przechowywana jest zmienna typu double mająca wartość π, moglibyśmy użyć unii liczby double i tablicy znaków (czyli bajtów).

/* założenie: double ma rozmiar 8 bajtów */ union u { double value; unsigned char byte[8]; }; union u number.value = 4.0 * atan(1.0); printf("Liczba pi jest zapisywana jako bajty "); for (int i = 0; i < 8; i++) printf("%02x", u.byte[0]);

Page 134: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

Użyteczne jest stosowanie struktury (z polami bitowymi) w unii:

struct bits { bool valid : 1; unsigned data1 : 7; unsigned status : 2; unsigned : 0; unsigned data2 : 7; }; union uu { uint32_t value; struct bits b; } variable; variable.b.valid = 1; variable.b.data1 = 0xb5; qux(variable.value);

Page 135: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

W powyższym przykładzie qux() jest funkcją, która przyjmuje tylko 32-bitowe liczby całkowite, przy tym mają są one „posklejane” z parobitowych fragmentów. Tworząc znacznik struct bits jawnie określiliśmy (po dwukropkach) jak wiele bitów ma być przydzielonych do każdego pola. Zero ma specjalne znaczenie – powoduje przeniesienie do następnego bajtu. Takie manipulowanie poszczególnymi bitami jest zwykle związane z obsługą hardware.63

63 Operacje na strukturach są nieatomowe, a same struktury mogą być zapisane w różnym

porządku bajtowym.

Page 136: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

Możemy np. zastosować jednocześnie tablicę, strukturę i unię do utworzenia tablicy struktur, do oszczędnego przechowywania różnego rodzaju danych:

struct info { char kind; /* kind == 0 oznacza że jest przechowywany integer */ /* kind == 1 oznacza że jest przechowywany floatpoint */ union { long int integer; double floatpoint; } } table[1000];

Element tablicy table zajmie 1 + 8 bajtów, bez zastosowania unii zająłby 1 + 4 + 8 bajtów. Daje to 30% oszczędność pamięci. Bardziej eleganckim rozwiązaniem byłoby, w powyższym przykładzie, zamiast zmiennej kind mogącej przyjmować wartości całkowite, zastosowanie zmiennej mogącej przyjmować wyłącznie dwie wartości, np. infoInteger, infoFloatpoint. W języku C służy do tego słowo kluczowe enum:

enum infoEnum {infoInteger, infoFloatpoint} var = infoFloatpoint;

Page 137: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

Wykład 7

Page 138: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

Wskaźniki

Czy wartość, umieszczoną „gdzieś w pamięci”, można użyć/zmodyfikować w jakiś taki sposób, aby nie używać zmiennej? Można. Jeżeli wiemy gdzie została zapisana i użyjemy adresów (numerów komórkek pamięci) pod którymi jest ona umieszczona. Ale adresy mogą być rozmaicie określane na różnych maszynach i są z nimi jeszcze inne problemy. Dlatego używa się zamiast nich tzw. wskaźników – abstrakcyjnego określenia gdzie w pamięci komputera można znaleźć interesujące nas dane. W języku C podstawowymi operatorami przeznaczonymi do pracy ze wskaźnikami są operator adresu & i operator wyłuskania * . Dodatkowo dla struktur stosuje się operator ->. Uwaga: znaki & i * mogą oznaczać także operację AND na bitach i zwykłe mnożenie. NAJWAŻNIEJSZE – wskaźnik do interesującej nas zmiennej możemy zapamiętać w innej (wskaźnikowej) zmiennej. Specyficzna dla języka C/C++ jest arytmetyka wskaźników.

Page 139: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

... int val; // val to jest zmienna typu int int *ptr; // ptr to jest zmienna takiego typu, że *ptr jest typu int // - czyli jest to zmienna wskaźnikowa do typu int // ale w uproszczeniu będziemy rozumieć ją jako „wskaźnik” // mamy dwie zmienne, ale zmienne te nie są jeszcze zainicjalizowane // - więc je zainicjalizujemy teraz ptr = &val; // pamięć dla val już została przydzielona, bierzemy „adres” // - od tej pory możemy używać *ptr zamiast zmiennej val *ptr = 1410; // wpisaliśmy wartość do zmiennej val, chociaż użyliśmy tylko // wskaźnika, a nie val printf(" %d \n", *ptr) // na ekranie powinnien ukazać się napis 1410 printf(" %d \n", val) // na ekranie powinnien ukazać się napis 1410 printf(" %p \n", ptr) // tym razem zobaczymy gdzie zapisana jest val ...

Page 140: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

W języku C wskaźniki pozwalają przekazywać wartości na zewnątrz funkcji inaczej niż przez wartość umieszczoną po return.

int foo(int val, int *ptr) { *ptr = val; val = val + 1; return (val == 10); } ... int i, j; ... j = foo( 3/10 + 10/3, &i)

Page 141: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

Do tablic, tak samo jak do zwykłych zmiennych, można mieć wskaźniki. Można też utworzyć tablicę, której każdym elementem będzie wskaźnik.

int x[1000]; /* tablica tysiąc-elementowa */ int y[2][2]; /* macierz 2x2 */ int (*ptrA)[1000]; /* wskaźnik do (tablicy o 1000 elementach int) */ int * ptrB [1000]; /* tablica (1000 wskaźników do int) */ int (*ptrC[1000])(void); /* tablica wskaźników do funkcji */

Wyobraźmy sobie, że mamy wskaźnik typu int*, czyli wskazujący na liczby całkowite, który na nic nie wskazuje jeszcze. Możemy oczywiście wpisać do niego adres istniejącej zmiennej, utworzonej w segmencie danych lub na stosie. Ale jak program może zwrócić się, o dodatkową pamięć, która nie jest związana z już istniejącą zmienną?

Page 142: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

W C służą do tego najczęściej dwie64 funkcje: malloc() i calloc(). Funkcja malloc() dostarcza wskaźnika do bloku pamięci o wielkości jaki sobie zażyczymy. Jeżeli to będzie możliwe. Jeżeli będzie to niemożliwe funkcja ta zwraca NULL. Funkcja calloc() jest w działaniu niemal taka sama jak malloc(), ale gwarantuje, że przydzielona pamięć będzie wypełniona zerami. Komplementarną funkcją jest jest free(), która mając wskaźnik do bloku pamięci zwalnia ten blok. Jest też możliwa zmiana wielkości bloku, do czego służy funkcja realloc(). Ale przede wszystkim musimy wiedzieć, ile pamięci chcemy dostać do dyspozycji. Pomaga w tym operator sizeof, który daje ilość pamięci w bajtach (oktetach) jaką zajmuje obiekt danego typu, zmienna czy wyrażenie, np. sizeof(int32_t) da wartość 4, podobnie sizeof j dałoby wartość 4, gdyby j było typu int32_t.

64 Istnieją inne funkcje, które także mogą dynamicznie przydzielać pamięć. Niektóre z nich wyszły z

bezpośredniego użycia np. sbrk(), inne są związane z konkretnymi systemami operacyjnymi, np. GetGlobalMemory(), jeszcze inne mogą sprawiać problemy w niektórych sytuacjach np. alloca(). W języku C++ nie jest zabronione używanie funkcji malloc() itp., ale zamiast nich używa się operatorów new i delete.

Page 143: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

#include <stdio.h> #include <stdlib.h> int main(void) { int* ptr = NULL; /* wskaźnik, tymczasowo równy NULL */ ptr = (int*)malloc(sizeof(int)); /* przydzielanie pamięci na jeden int */ if (ptr != NULL) /* jeżeli się udało */ { *ptr = 1; printf("%d\n", *ptr); free(ptr); /* pamięć może już być zwolniona*/ } else / * nie udało się */ { /* pamięć nie musi być zwalniana */ printf("brak pamięci\n"); } return 0; }

Page 144: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

Program powyżej ilustruje podstawy sposób dynamicznego przydziału pamięci do wskaźników. Ponieważ funkcja malloc() zwraca wskaźnik do void, to użyta jest w nim konwersja typu zmiennych, czyli operacja rzutowania (int*). W języku C/C++ można dokonać konwersji jednego typu na drugi za pomocą operacji:

zmienna_typu_2 = (nazwa_typu_2) wyrażenie_lub_zmienna_typu_1;

Operację taką nazywa się rzutowaniem, a umieszczoną w nawiasach nazwę typu określa jako operator rzutowania. W języku C rzutowanie wyniku funkcji malloc() nie jest niezbędne, w C++ jest konieczne.

Page 145: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

Zamiast przydzielać tylko tyle pamięci, ile potrzeba na jedną zmienną int, moglibyśmy przydzielić jej tyle, ile potrzeba na 100 liczb. W ten sposób utworzylibyśmy tablicę. Czym taka tablica różni się w użyciu od „normalnych” tablic? Okazuje się, że niczym. Można w normalny sposób ja indeksować, można używać jako argumentu funkcji.

double* a = malloc(100*sizeof(double)); ... for (i = 0; i < 100; i++) a[i] = sin(2.0*3.1415927*(double)i/100.0) ... free(a)

Page 146: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

Arytmetyka wskaźników jest mechanizmem języka C/C++, który pozwala mając wskaźnik ptr i wyrażenie całkowitoliczbowe num obliczać ptr + num oraz ptr - num . Operator wyłuskania * działając na zmienną a z przykładu powyżej daje po prostu a[0], czyli *a to to samo co a[0]. Wyrażenie *(a+1) to to samo co a[1]. I tak dalej. Co ciekawsze, można byłoby np. zamiast a[1] pisać 1[a], bo jest to to samo co *(1+a), czyli *(a+1), czyli a[1]. Podobnie, jeżeli wskaźnik b = a + 10, to *(b - 9) jest tym samym co *(a + 1), czyli znowu a[1]. Istotne w arytmetyce wskaźników jest to, że dodawanie (lub odejmowanie) liczby całkowitej przesuwa wskaźnik w pamięci o tyle bajtów, ile ma ich typ wskazywany. Czyli dla liczb czterobajtowych dodanie +1 przesunie wskaźnik int* o cztery bajty, ale gdyby wskaźnik był typu double* to przesunięcie będzie ośmiobajtowe. Zawsze dla wskaźnika typ* jednostką jest sizeof(typ).

Page 147: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

Wykorzystajmy to do stworzenia dynamicznych tablic w języku C. Do przydzielenia bloku pamięci na przechowywanie n wartości typu double zamiast malloc(n*sizeof(double)) użyjemy calloc(n,sizeof(double)). Wskaźnik jaki zwróci calloc() cofniemy o jedną pozycję tak, aby na początek przydzielonego bloku trafiał dopiero element numer 1. W ten sposób otrzymamy tablice z elementami numerowanymi od 1 do n: v[1], v[2], …, v[n]. Macierz, czyli dwuwymiarową tablicę, skonstruujemy w dwóch krokach. Najpierw stworzymy tablicę wskaźników o długości odpowiadającej liczbie wierszy macierzy. Potem do każdy wskaźnik w tej tabeli ustawimy na przydzielony w pętli for wektor. Konsekwentnie stosując cofanie wskaźników będziemy mieli macierze o indeksach zaczynających się od 1, czyli a[1][1], a[1][2], …, a[n][1], a[n][2], …, a[n][m].

Page 148: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

W programie do pary funkcji konstruujących new_vector() i new_matrix() potrzebna będzie para funkcji do dekonstrukcji free_vector() i free_matrix(). Aby pokazać, jak to wszystko działa, w programie przykładowym są jeszcze trzy nieskomplikowane funkcje: matrix_read(), matrix_add() i matrix_write() do, odpowiednio, czytania, dodawania i wypisywania macierzy. Program tworzy 3 macierze 2x2, ale oczywiście moglibyśmy próbować utworzyć macierze 1000x1000 lub większe (jeżeli mielibyśmy cierpliwość wpisywać trzy miliony elementów lub mieli gotowy plik z co najmniej taką ilością liczb).

Page 149: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

/* Dynamiczne tablice wersja 0.1, © 2011 Sławomir Marczyński */ #include <stdio.h> #include <stdlib.h> double* new_vector(int n) { return (double*)calloc(n,sizeof(double)) - 1; } void free_vector(int n, double* v) { free(v + 1); } double** new_matrix(int n, int m) { int i; double** ptr = (double**)calloc(n,sizeof(double*)) - 1; for (i = 1; i <= n; i++) ptr[i] = new_vector(m); return ptr; } void free_matrix(int n, int m, double** matrix) { int i; for (i = 1; i <= n; i++)

Page 150: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

free_vector(m,matrix[i]); free(matrix+1); } void matrix_read(int n, int m, double** a) { int i,j; for (i = 1; i <= n; i++) for (j = 1; j <= m; j++) scanf("%lf", &a[i][j]); } void matrix_write(int n, int m, double** a) { int i,j; for (i = 1; i <= n; i++) { for (j = 1; j <= m; j++) printf(" %lf", a[i][j]); printf("\n"); } } void matrix_add(int n, int m, double** a, double** b, double** c) { int i,j;

Page 151: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

for (i = 1; i <= n; i++) for (j = 1; j <= m; j++) c[i][j] = a[i][j] + b[i][j]; } int main(void) { double** macierzP = new_matrix(2,2); double** macierzQ = new_matrix(2,2); double** macierzT = new_matrix(2,2); matrix_read (2,2, macierzP); matrix_read (2,2, macierzQ); matrix_add (2,2, macierzP, macierzQ, macierzT); matrix_write(2,2, macierzT); free_matrix(2,2, macierzP); free_matrix(2,2, macierzQ); free_matrix(2,2, macierzT); }

Page 152: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

Zaletą jest, że dla macierzy nie musimy mieć tyle pamięci ile potrzebuje jedna macierz jako monobloku, wystarczy jeżeli jest dość bloków pamięci odpowiadających jednemu wierszowi macierzy. Wadami są: nieco większe zużycie pamięci (dodatkowo na tablicę wskaźników do wierszy) i nieco wolniejsze działanie (aby wyciągnąć element a[1][1] potrzeba sięgnąć dwa razy do pamięci). Aby uprościć program nie sprawdzaliśmy, czy udało się nam dostać pamięć, o jaka poprosiliśmy wywołaniem funkcji calloc(). Należałoby np. tak ulepszyć funkcję new_vector() i podobnie new_matrix() :

double* new_vector(int n) { double* ptr = (double*)calloc(n,sizeof(double)); if (ptr != NULL) return ptr - 1; else { new_vector_error(); /* lub co? innego, co zg?osi b??d dzia?ania */ return NULL; } }

Page 153: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

Można też dodać alokowanie macierzy o liczbach całkowitych, zespolonych itd. Gotową mini-bibliotekę można znaleźć w Internecie65 pod adresem http://www.nr.com/public-domain.html Używając języka C każdy przydzielony dynamicznie blok pamięci musimy zwolnić. Jeżeli tego nie zrobimy a nie będziemy mieli wskaźnika do niego, to i tak będzie on zarezerwowany do użycia tak długo, jak długo program będzie nie zakończony, powodując tzw. wyciek pamięci,66 które nawet niewielkie, prowadzą z czasem do złego działania komputera. Język Java automatycznie zapobiega wyciekom pamięci, a zwalnianie niepotrzebnych już jej bloków następuje automatycznie przez mechanizm odświecania (garbage collection). W języku C jest i taka możliwość, ale wymaga stosowania dodatkowych bibliotek nie wchodząc w skład standardu. Warto zauważyć, że każdy wątek ma swój własny stos, ale sterta jest wspólna. Dlatego zmienne tworzone dynamicznie mogą sprawiać nieco więcej problemów niż zwykłe zmienne automatyczne tworzone na stosie.

65 Jest to wyjątkowa część biblioteki Numerical Recipes udostępniona do użytku publicznego (public

domain). 66

Jeżeli tego rodzaju problem wystąpi w usłudze systemowej lub bibliotece współdzielonej, to wyciek pamięci może trwać aż do restartu systemu.

Page 154: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

Wyobraźmy sobie, że mamy napisać program, który czyta bliżej nieokreśloną ilość danych (ale mieszczącą się w pamięci komputera) i potem ma z nich obliczyć średnią i odchylenie od średniej. Aby elastycznie zwiększać rozmiar tabelki z liczbami potrzebujemy funkcji, takiej jak realloc(), zmieniającej rozmiar bloku pamięci a jednocześnie nie niszczącej już zapamiętanych wartości. W pseudokodzie nasz program będzie wyglądał tak:

Utwórz tabelkę mogącą pomieścić dokładnie jedną liczbę

Powtarzaj wczytywanie jednej liczby do tabelki jak długo się uda

{

zwiększ o jeden licznik przeczytanych wartości

powiększ tabelkę o jedna pozycję

}

Oblicz średnia arytmetyczną wartości w tabelce

Page 155: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

/* Elastyczna tablica wersja 0.1, © 2011 Sławomir Marczyński */ #include <stdio.h> #include <stdlib.h> double mean(int n, double* a){ int i; double s = 0.; for (i = 0; i < n; i++) s += a[i]; return s/n; } int main(void){ int n = 0; double* table = (double*)calloc(1,sizeof(double)); while (scanf("%lf", &table[n]) == 1){ n++; table = realloc(table, (n+1)*sizeof(double) ); } printf("%lf", mean(n,table) ); free(table); return 0; }

Page 156: Wykład 1 - slawek.zut.edu.plslawek.zut.edu.pl/fileadmin/2012-2013/slawek-w01-w07.pdf · Romualda Marczyńskiego w roku 1968. teoria informacji algorytmy i struktury danych programowanie

Jak widać, cel nasz osiągnęliśmy w bardzo prosty sposób. Nawet trochę zbyt prosty: wielokrotne wywoływanie funkcji realloc() jest prawidłowe, ale znacznie spowalnia program. Większą szybkość działania dałoby powiększanie, w razie potrzeby, tabeli table nie o jeden element double, ale np. od razu od 1024 elementów, czyli porcjami po 8KB.