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