Inicjalizacja zmiennych, operatory
Transkrypt
Inicjalizacja zmiennych, operatory
Podstawy Programowania Wykład drugi: Inicjalizacja zmiennych, operatory. 1. Inicjalizacja zmiennych Zanim użyjemy zmiennej w programie, powinniśmy nadać jej jakąś wartość początkową (zainicjalizować zmienną). Brak inicjalizacji jest jednym z najczęstszych błędów, jakie popełniają programiści. Inicjalizację zmiennej możemy wykonać na kilka sposobów. Domyślnie wszystkie zmienne globalne (zadeklarowane w sekcji var programu głównego) mają zerową wartość początkową (tzn. w przypadku zmiennej typu byte jest to 0, w przypadku real wartość 0.0, w przypadku char znak o kodzie ASCII równym 0). Jeśli chcemy, aby zmienna miała wartość zerową wystarczy zadeklarować ją jako zmienną globalną. Jeśli chcemy nadać jej jakąś wartość różną od zera możemy postąpić na dwa sposoby: użyć instrukcji przypisania (:=) lub zadeklarować ją jako zmienną zainicjalizowaną. Oto przykład programu, który stosuje oba sposoby inicjalizacji zmiennej. Instrukcja przypisania powoduje program inicjalizacja; nadanie zmiennej znajdującej się po const jej lewej stronie wartości znajdującej y:integer = -1; się po jej prawej stronie. Ta wartość var może być stałą, wartością wyrażenia, x:integer; lub wartością innej zmiennej. begin Zmienną inicjalizowaną deklarujemy x:=5; w sekcji programu rozpoczynającej writeln('Wartość x ',x); się słowem kluczowym const (w tym writeln('Wartość y ',y); wypadku może ona również wyy:=-4; stępować po części var). Deklaracja writeln('Nowa wartość y ',y); tej zmiennej jest podobna do deklareadln; racji stałej, z tym, że podajemy typ. end. Uruchamiając program można przekonać się, że jest to rzeczywiście zmienna, a nie stała. W czwartym od końca wierszu programu następuje zmiana jej wartości, co w przypadku stałej nie jest możliwe. Zamiast inicjalizować zmienną bezpośrednio w programie, możemy poprosić użytkownika o podanie wartości, jaką chce nadać zmiennej. Najprościej zrobić to przy pomocy procedury readln. Jeśli chcemy, aby użytkownik nadał zmiennej o nazwie a jakąś wartość, w treści programu umieszczamy zapis: readln(a); Procedura readln zatrzyma program do czasu, aż użytkownik wprowadzi odpowiednią wartość i naciśnie klawisz Enter. Po uzyskaniu w ten sposób wartości, procedura przypisuje ją zmiennej a. Jeśli wywołamy tę procedurę bez żadnej zmiennej, to spowoduje ona tylko zatrzymanie programu do czasu naciśnięcia przez użytkownika wspomnianego wcześniej klawisza. Istnieje również możliwość „równoczesnego” zainicjalizowania przez tę procedurę dwóch zmiennych np.: a i b. Wó2 wczas należy użyć tej procedury następująco: readln(a,b); Użytkownik programu powinien podać dwie wartości rozdzielone spacją i nacisnąć Enter. Zalecam jednak używanie procedury readln z pojedynczymi zmiennymi. Aby wypisać wartość zmiennej na ekran możemy użyć procedury write lub writeln. Druga różni się od pierwszej tym, że po wypisaniu komunikatu na ekran umieszcza kursor w następnym wierszu ekranu. Jeśli chcemy wypisać na ekran wartość zmiennej a możemy zrobić to następująco: writeln(a);. Jeśli chcemy dodać komunikat, to umieszczamy go w apostrofach i po przecinku wymieniamy nazwę zmiennej, której wartość chcemy wypisać: writeln('Wartość zmiennej a to ',a);. W przypadku, kiedy wartość zmiennej jest liczbą całkowitą lub naturalną, możemy określić również na ilu miejscach na ekranie ma zostać wypisana, np.: writeln('Wartość zmiennej a to ',a:3); W tym przypadku zostaną zarezerwowane trzy miejsca na ekranie na wypisanie wartości zmiennej. Dla wartości zmiennoprzecinkowej możemy podać również, ile miejsc chcemy zarezerwować na część ułamkową: writeln('Wartość zmiennej a ',a:3:2); - w tym przypadku zostaną zarezerwowane trzy miejsca na wypisanie wartości liczby, w tym dwa na część ułamkową. Oto program pokazujący kilka sposobów inicjalizacji zmiennych różnego typu: program inicjalizacja_2; uses crt; var logiczna:boolean; znak:char; liczba_naturalna:byte; liczba_calkowita:shortint; liczba_rzeczywista, lrz:real; lancuch:string; begin clrscr; logiczna:=TRUE; lrz:=3e-10; {Liczba e oznacza podstawę potęgi 10, czyli jest to trzy razy dziesięć do potęgi minus dziesiątej} liczba_rzeczywista:=0.03; znak:=#65; {65 to kod ASCII dużej litery A, poprzedzamy go znakiem #} writeln('Wartość lrz bez formatowania ',lrz); writeln('Wartość liczba_rzeczywista z formatowaniem :3:2 ', liczba_rzeczywista:3:2); writeln('Znak: ',znak); lancuch:='Napis'; 3 writeln(lancuch); liczba_naturalna:=4; liczba_calkowita:=liczba_naturalna; writeln(liczba_naturalna:4); writeln(liczba_calkowita:10); znak:='a'; {W ten sposób też możemy inicjalizować zmienną typu char – podając znak w apostrofach} writeln(znak); readln; end. Oprócz tych sposobów inicjalizacji zmiennych, które zostały uwzględnione 1 w programie możemy również nadać zmiennej wartość pseudolosową . W tym celu najpierw musimy wywołać procedurę randomize, poza wszelkimi instruk2 cjami iteracyjnymi , a następnie dla uzyskania konkretnej wartości musimy wywołać funkcję random z parametrem, będącym wartością mieszczącą sie w typie word, np.: a:=random(10); gdzie a jest zmienną typu całkowitego. Taki zapis oznacza, że tej zmiennej zostanie przypisana pseudolosowa wartość całkowita z przedziału od 0 do 9. Jeśli chcemy wylosować tylko wartość ułamkową, to musimy wywołać random, bez żadnego parametru, wówczas losowane będą wartości z przedziału od [0,1). Jeśli inicjalizując zmienna pewną liczbą, wygodniej będzie ją nam zapisać w kodzie szesnastkowym, to możemy to uczynić poprzedzając jej wartość znakiem $, np.: x:=$5a; Jeśli zmiennej nadajemy w programie (obojętnie w jaki sposób) kilkukrotnie wartość (a tak najczęściej robimy), to tylko pierwsze przypisanie jest nazywane inicjalizacją. 2. Operatory i wyrażenia Język Pascal oferuje szereg operatorów, które umożliwiają budowanie wyrażeń i przeprowadzanie operacji na wartościach zmiennych. Poniższa tabela przedstawia najważniejsze z nich, wraz z ich priorytetami. Operator Priorytet Kategoria +,-,not 1 (najwyższy) jednoargumentowe *,/,div,mod,and,shl,shr 2 multiplikatywne +,-,or,xor 3 addytywne =,<>,<,>,<=,>= 4 (najniższy) relacyjne Priorytet określa kolejność wykonywania operatorów w wyrażeniu (kolejność 1 Ponieważ sposób generowania takich wartości nie gwarantuje ich „pełnej” losowości, to nie mówimy o nich, że są losowe, tylko pseudolosowe. 2 O tych instrukcjach dowiemy się na następnym wykładzie. 4 działań). Najwyższy mają operatory jednoargumentowe. Operator – powoduje zmianę znaku liczby na przeciwny, + nie zmienia wartości liczby (został wprowadzony jako dopełnienie poprzedniego operatora), natomiast operator not jest operatorem negacji – zamienia wartość każdego bitu liczby na przeciwną (0 na 1 i odwrotnie). Niższy priorytet mają operatory multiplikatywne: * oznacza mnożenie, / dzielenie, div dzielenie całkowite (bez reszty), mod – operacja modulo (reszta z dzielenia), and – operator mnożenia logicznego, shl – mnożenie liczby przez potęgę dwójki (przesunięcie jej reprezentacji binarnej o zadaną ilość miejsc w lewo), shr – dzielenie liczby przez potęgę dwójki (przesunięcie jej reprezentacji binarnej o zadaną ilość miejsc w prawo). Kolejne są operatory addytywne. Operator + oznacza dodawanie lub, jeśli jego argumentami są zmienne typu string, łączenie zawartości tych zmiennych (konkatenację). Operator or oznacza sumę logiczną, operator xor różnicę symetryczną. Najniższe priorytety mają operatory relacyjne: = to operator porównania (nie mylić z instrukcją przypisania !!), <> operator „różne”, < operator „mniejsze”, > „operator większe”, <= mniejsze lub równe, >= większe lub równe. Operatory te zwracają wartość logiczną i mogą porównywać nie tylko zmienne liczbowe, ale również zmienne typu string, char i boolean. Należy pamiętać, że operacje arytmetyczne są wykonywane modulo zakres typu zmiennej. Przykład: program operatory_1; uses crt; var a:byte; begin clrscr; a:=255; a:=a+1; writeln(a); readln; end. W wyniku wykonania tego programu na ekranie zobaczymy nie liczbę 256, lecz zero. Dlaczego ? - liczba 256 nie mieści się w zakresie typu byte i dlatego zostanie „obcięta” do wartości zero. Jeśli zamiast o jeden zwiększylibyśmy jej wartość o 2, to otrzymalibyśmy w wyniku liczbę 1. Jeśli jednak umieścilibyśmy wyrażenie a+1 bezpośrednio w instrukcji writeln: writeln(a+1); to otrzymalibyśmy poprawny wynik. Dzieje się tak dlatego, że procedura write (lub writeln) zakłada dla wyrażeń w których występują zmienne typu byte i word, że wynik jest typu word. Oznacza to, że problemy wystąpią dopiero przy wartościach wyrażeń 5 większych niż 65535. Podobne zjawisko zachodzi również dla innych typów zmiennych: program operatory_2; uses crt; var x:shortint; begin clrscr; x:=-128; x:=x-1; writeln(x); readln; end. Wyjaśnienia wymaga sposób działania operatorów and, or, xor oraz shl i shr. Działanie operatora and dotyczy wszystkich bitów jego argumentów. Jeśli bity argumentów, znajdujące się na tej samej pozycji są równe jeden, to odpowiadający im bit wyniku będzie miał wartość 1, w innych przypadkach wartość 0. Np.: 5 and 3 da wartość 1, bo: 00000101 and 00000011 00000001 Jeśli argumentami tego operatora są wartości logiczne, to zwraca on wartość TRUE, wtedy i tylko wtedy, kiedy oba argumenty też są równe TRUE. Operator or również działa na poziomie bitów, ale według innej reguły. Bit znajdujący się na określonej pozycji w wyniku ma wartość zero, wtedy i tylko wtedy, gdy oba odpowiadające mu bity argumentów mają też wartości zero. W przeciwnym wypadku ma wartość 1. Np.: 5 or 3 daje wartość 7, bo: or 00000101 00000011 00000111 Jeśli argumentami tego operatora są wartości logiczne, to zwraca on wartość FALSE, wtedy i tylko wtedy, kiedy oba argumenty też są równe FALSE. Operator xor ustawia wartość odpowiedniego bitu wyniku na wartość 1, wtedy 6 i tylko wtedy, gdy odpowiadające mu bity argumentów są różne, w przeciwnym wypadku ten bit ma wartość 0. Np. 5 xor 3 daje wartość 6, bo: 00000101 xor 00000011 00000110 Jeśli argumentami tego operatora są wartości logiczne, to zwraca on wartość TRUE, wtedy i tylko wtedy, kiedy wartości argumentów się różnią, a FALSE kiedy są takie same. Operator shr jest operatorem przesunięcia w prawo, czyli dzielenia całkowitego przez potęgę dwójki. Wyrażenie 5 shr 2 daje wartość 1, bo 00000101 shr 2 = 00000001 (odpowiada wyrażeniu 5 div 4) Jak można zaobserwować wartości najbardziej skrajnych bitów po prawej stronie są tracone, natomiast bity po lewej stronie przyjmują wartość zero. Podobnie działa operator shl, ale on dokonuje przesunięcia w lewo, czyli mnożenia przez potęgę dwójki. Oto przykład 5 shl 2 = 20, bo 00000101 shl 2 = 00010100 (odpowiada wyrażeniu 5*4) Język Trubo Pascal, pozwala również na budowanie wyrażeń logicznych, w których używane są operatory relacyjne. Oto przykład programu, w którym umieszczono dwa takie wyrażenia: program operatory_3; uses crt; var a,b,c:boolean; begin clrscr; a:=false; b:=false; c:=true; writeln(a=b or c); {false} writeln((a=b) or c); {true} readln; end. 7 3. Inne działania 3 Oprócz operatorów Turbo Pascal dostarcza programistom funkcji i procedur , które pozwalają na wykonanie innych operacji na zawartościach zmiennych. Do nich między innymi należą: succ - funkcja zwracająca następnik, działa tylko dla typów porządkowych, np.: b := succ(a); pred - funkcja zwracająca poprzednik, działa tylko dla typów porządkowych, np.: b := pred(a); ord - funkcja zamienia wartość typu porządkowego na wartość całkowitą, najczęściej służy do uzyskiwania kodu ASCII znaku, np.: x:=ord('a'); chr - funkcja zmienia wartość całkowitą na znak writeln(chr(97)); abs - funkcja zwraca wartość bezwzględną liczby writeln(abs(-5));, int - funkcja, zwraca część całkowitą liczby zmiennoprzecinkowej, wartość zwracana jest typu real; frac - jak wyżej, ale zwraca część ułamkową, trunc - podobnie jak int zwraca część całkowitą liczby rzeczywistej, ale jako wartość typu integer, high - funkcja ta zwraca maksymalną wartość, jaką da się zapisać w zmiennej danego typu porządkowego, low - jak wyżej, ale zwraca wartość minimalną, 3 Te pojęcia będą oczywiście wyjaśnione na innych wykładach, ale żeby zrozumieć zawartość tego rozdziału wprowadźmy kilka nieformalnych definicji. Wywołanie procedury ma postać: NazwaProcedury(parametry); parametrami mogą być zarówno wartości, jak i zmienne, z tym że nie zawsze możliwa jest dowolność, tzn. w niektórych przypadkach parametry są parametrami wyjściowymi i muszą być zmiennymi. Wywołanie funkcji ma zaś postać: Zmienna := NazwaFunkcji(parametry); Funkcja zwraca zawsze jakąś wartość, która jest przypisywana zmiennej, lub ignorowana (wówczas nie występuje instrukcja przypisania i jej lewa strona). Zarówno w przypadku procedur, jak i funkcji nie zawsze musi występować lista parametrów, niektóre z nich po prostu nie wymagają żadnych parametrów. 8 round - funkcja wykonuje zaokrąglenie części ułamkowej zmiennoprzecinkowej, wartość wynikowa jest typu longint, liczby str – procedura ta zamienia wartość liczbową na łańcuch znaków, np.: str(11.58:4:2, nap); gdzie nap jest zmienną typu string, val – dokonuje konwersji w drugą stronę, tzn. zamienia łańcuch znaków na liczbę, np.: val('25.5',liczba,blad), gdzie liczba jest zmienną typu real (może być też zmienną typu integer), blad zmienną typu integer. W razie niepowodzenia konwersji w zmiennej blad będzie zapisany numer pozycji znaku, który spowodował błąd. sin – funkcja zwracająca wartość sinusa dla określonej wartości kąta, parametr i wartość zwracana są typu real. Przyjmuje, że kąt jest wyrażony w radianach! cos – jak wyżej, ale zwracana wartość cosinusa. x exp – funkcja zwracająca wartość funkcji e (eksponenty). 4 ln – funkcja zwracająca wartość logarytmu naturalnego . sizeof – funkcja, która podaje rozmiar zmiennej (liczbę bajtów którą zajmuje w pamięci). sqr – funkcja zwracająca kwadrat wartości podanej jej przez parametr. sqrt – funkcja zwracająca pierwiastek kwadratowy wartości podanej jej przez parametr. odd – funkcja zwraca wartość TRUE, jeśli podana jej liczba całkowita, mieszcząca się w zakresie typu longint jest wartością nieparzystą. inc – procedura zwiększająca wartość zmiennej podanej jej jako pierwszy parametr, o wartość, która jest jej podana jako drugi parametr. Jeśli drugi parametr nie występuje, to wartość zmiennej jest zwiększana o jeden. dec – jak wyżej, ale wartość zmiennej jest zmniejszana, hi – podaje wartość starszego bajta zmiennych typu word i integer (2 baj4 Jeśli chcemy obliczyć wartość funkcji ax, gdzie a i x są liczbami rzeczywistymi i a>0, to możemy skorzystać z wyrażenia exp(x*ln(a)) 9 towych). lo – jak wyżej, ale podaje wartość młodszego bajta. swap – zamienia miejscami wartości bajtów zmiennych typu word i integer. 4. Zgodność i konwersje typów zmiennych Załóżmy, że mamy dane dwie zmienne. Pierwsza nazywa się x i jest typu T1, a druga y i jest typu T2. Przypisanie x:=y; jest poprawne jeśli spełniony jest któryś z warunków: typy T1 i T2 są identyczne, typ T1 i T2 są zgodnymi typami porządkowymi, a wartość zmiennej y jest jedną z możliwych wartości w typie T1, typy T1 i T2 są typami zmiennoprzecinkowymi i wartość zmiennej y po zaokrągleniu jest jedną z możliwych wartości w typie T1, typ T1 jest typem zmiennoprzecinkowym, a typ T2 jest typem całkowitym, T1 i T2 są typami całkowitymi, T1 jest typem łańcuchowym (string), a T2 jest typem znakowym. W innych przypadkach musimy zastosować konwersję typów wartości. Dla zmiennych porządkowych możliwa jest konwersja według schematu: x := NazwaTypu(y); przykładowo x:=Integer('A'); Inne konwersje wykonujemy korzystając, z funkcji i procedur, które zostały opisane w poprzednim rozdziale. Jeśli nie dokonamy takiej konwersji, to kompilator zgłosi błąd. 10