Delphi :: Kompendium :: Rozdział 2 - 4programmers.net http://4programmers.net/Delphi/Kompendium/Rozdzia%C5%82_2
Logowanie | Rejestracja | Forum | Pomoc | Reklama | Szukaj
Strona główna :: Delphi :: Kompendium
Rozdział 2
Delphi C/C++
Edytuj Historia
Artykuły Artykuły
Kompendium FAQ
Gotowce C#
Wprowadzenie
Język Object Pascal FAQ
Assembler
.NET
FAQ
Turbo Pascal
(X)HTML
Rozdział ten stanowi wstęp do programowania w Delphi. Poznanie zasad działania języka Object Pascal jest FAQ
CSS
niezbędnym warunkiem kontynuowania nauki środowiska Delphi. Wszystkie informacje postaram się przekazać
PHP
JavaScript
dokładnie, przedstawiając je krok po kroku. Zacznę oczywiście od spraw podstawowych.
FAQ
Z pogranicza
Java
Algorytmy
FAQ
WICEJ
Spis treści
1 Plik zródłowy projektu
1.1 Najprostszy program
2 Podstawowa składnia
Delphi Programy
2.1 Wielkość liter
C/C++ Dokumentacja
2.2 Pamiętaj o średniku!
Turbo Pascal Kursy
2.3 Bloki begin i end
Assembler Komponenty
2.4 Dyrektywa program
PHP WICEJ
3 Komentarze
4 Zmienne
4.1 Deklaracja zmiennych
4.2 Typy zmiennych
4.3 Deklaracja kilku zmiennych
4.4 Przydział danych
4.4.1 Przydział dynamiczny
5 Stałe
5.1 Domyślne typy stałych
6 Używanie stałych i zmiennych w programie
7 Tablice danych
7.1 Tablice jako stałe
7.2 Tablice wielowymiarowe
7.3 Tablice dynamiczne
7.3.1 Polecenia Low i High
8 Operatory
9 Aplikacje konsolowe
10 Instrukcje warunkowe
10.1 Instrukcja if..then
10.1.1 Pobieranie tekstu z konsoli
10.1.2 Kilka instrukcji po słowie then
10.1.3 Kilka warunków do spełnienia
10.2 Instrukcja case..of
10.2.1 Zakresy
10.2.2 Brak możliwości korzystania z łańcuchów
10.2.3 Kilka instrukcji
10.3 Instrukcja else
10.3.1 Kiedy stosować średnik, a kiedy nie ?
10.3.2 Kilka instrukcji if i else
10.3.3 Kilka instrukcji po słowie begin
10.3.4 Instrukcja else w case..of
11 Procedury i funkcje
11.1 Procedury
12 Funkcje
12.1 Zmienne lokalne
12.2 Parametry procedur i funkcji
12.2.1 Kilka parametrów procedur lub funkcji
12.3 Parametry domyślne
12.3.1 Parametry tego samego typu a wartości domyślne
12.3.2 Kolejność wartości domyślnych
12.4 Przeciążanie funkcji i procedur
12.5 Typy parametrów przekazywanych do procedur i funkcji
12.5.1 Przekazywanie parametrów poprzez stałą
12.5.2 Przekazywanie parametrów przez referencję
12.5.3 Dyrektywa out
12.6 Procedury zagnieżdżone
13 WÅ‚asne typy danych
13.1 Tablice jako nowy typ
14 Aliasy typów
15 Rekordy
15.1 Przekazywanie rekordów jako parametr procedury
15.2 Deklaracja rekordu jako zmienna
15.3 Instrukcja packed
16 Instrukcja wiążąca with
17 Moduły
17.1 Tworzenie nowego modułu
17.2 Budowa modułu
17.2.1 Nazwa
17.2.2 Sekcja Interface
17.2.3 Sekcja Implementation
17.3 Włączanie modułu
17.4 Sekcja Initialization oraz Finalization
18 Konwersja typów
19 Rzutowanie
20 Pętle
20.1 Pętla for..do
20.1.1 Odliczanie od góry do dołu
20.2 Pętla while..do
20.3 Pętla repeat..until
1 z 32 2009-03-14 15:25
Delphi :: Kompendium :: Rozdział 2 - 4programmers.net http://4programmers.net/Delphi/Kompendium/Rozdzia%C5%82_2
20.4 Procedura Continue
RSS | Forum | Pastebin |
20.5 Procedura Break
Regulamin | Pomoc | Usuń
21 Zbiory
Copyright © 2000-2006 by Coyote Group 0.9.3-pre3
cookies | Prawa autorskie |
21.1 Przypisywanie elementów zbioru
Czas generowania strony: 9.0395 sek. (zapytań SQL:
Kontakt | Reklama)
21.2 Odczytywanie elementów ze zbioru
12
21.2.1 Zaprzeczanie
21.2.2 Przekazywanie zbioru jako parametru procedury
21.3 Dodawanie i odejmowanie elementów zbioru
21.3.1 Include i Exclude
22 Wskazniki
22.1 Tworzenie wskaznika
22.2 Przydział danych do wskazników
22.2.1 Uzyskiwanie adresów zmiennej
22.3 Do czego to służy?
22.3.1 Tworzenie wskazników na rekordy
22.4 Przydział i zwalnianie pamięci
22.5 Wartość pusta
23 Pliki dołączane
24 Etykiety
25 Podsumowanie
W tym rozdziale:
poznasz podstawową składnię języka Object Pascal;
nauczysz się pisać programy konsolowe;
poznasz takie niezbędne pojęcia, jak pętle, instrukcje warunkowe czy zmienne.
Plik zródłowy projektu
Z menu File wybierz polecenie New/Application, co spowoduje utworzenie nowego projektu powinieneÅ›
pamiętać to z poprzedniego rozdziału.
W rozdziale tym porzucimy na chwilę projektowanie obiektowe (wizualne), nie będzie tu więc przykładów
wykorzystania komponentów tym zajmiemy się w rozdziale trzecim.
Z menu File wybierz polecenie Close zostaniesz zapytany, czy nie chcesz zapisać zmian w pliku Unit1.pas.
Kliknij przycisk No nie chcemy zapisywać pliku formularza. W tym momencie Edytor kodu powinien zostać
zamknięty. Z menu Project wybierz View Source. Polecenie to spowoduje wyświetlenie w Edytorze kodu
zawartości pliku głównego *.dpr.
Zapisz projekt pod nazwą dprFile. Zauważ, że tym razem nie zostałeś zapytany o nazwę formularza, gdyż
wcześniej zamknęliśmy go.
Zawartość pliku DPR przedstawiona jest w listingu 2.1.
Listing 2.1. Zawartość pliku DPR
program dprFile;
uses
Forms;
{$R *.res}
begin
Application.Initialize;
Application.Run;
end.
Kod przedstawiony powyżej jest generowany automatycznie przez Delphi. Nie przejmuj się nim na razie nie
wszystko będzie nam potrzebne.
Najprostszy program
Rozłożymy zawartość pliku głównego na części, dzięki czemu będziesz miał możliwość dowiedzenia się, jakie
funkcje pełnione są przez konkretne elementy kodu zródłowego.
Kod zródłowy najprostszego do napisania programu przedstawiony jest poniżej:
end.
To nie żart! Najprostszy program składa się właśnie z instrukcji end, z kropką na końcu. Możesz to sprawdzić
naciśnij klawisz F9, uruchamiając w ten sposób program. Oczywiście żadne polecenia oprócz end nie są
wpisane, dlatego program zaraz po uruchomieniu zostanie zamknięty.
Podstawowa składnia
Kod zródłowy musi składać się z określonych poleceń, zakończonych określonymi znakami. Nie można
pozostawić w kodzie żadnego bałaganu nawet pominięcie jednego znaku czy zwykła literówka mogą
spowodować niemożliwość uruchomienia programu. Na szczęście Delphi dysponuje na tyle dobrym
kompilatorem, że miejsce błędu zostanie wskazane, a problem opisany. Przykładowo brak słowa kluczowego
end przy próbie kompilacji spowoduje wskazanie błędu: [Error] dprMin.dpr(3): Declaration expected but end
of file found.
Zapamiętaj pierwszą zasadę: program musi być zawsze zakończony poleceniem end. (kropka na końcu!).
Wielkość liter
W języku Object Pascal w odróżnieniu od C++ czy Javy wielkość liter nie jest istotna. Dla kompilatora nie
jest istotne, czy nazwa funkcji będzie pisana w taki czy inny sposób. Na przykład polecenie ShowMessage
będzie znaczyło to samo, co showmessage. Można je także zapisywać w inny sposób:
showMessage
showMEssaGe
2 z 32 2009-03-14 15:25
Delphi :: Kompendium :: Rozdział 2 - 4programmers.net http://4programmers.net/Delphi/Kompendium/Rozdzia%C5%82_2
itd....
Nie jest zalecane pisanie kodu z wykorzystaniem jedynie małych liter - np. showmessage.
Wielu początkujących programistów, zafascynowanych nauką Delphi, nie pamięta o sposobie
pisania kodu projektanci ci nie stosują wcięć w kodzie, a wszystkie polecenia piszą
małymi literami. Uwierz mi, że właśnie po sposobie pisania kodu można rozpoznać
początkującego programistę ci bardziej zaawansowani przyjęli takie zasady tworzenia
kodu, aby był on bardziej przejrzysty. Z moją propozycją pisania kodu możesz zapoznać się
w dodatku A.
Pamiętaj o średniku!
Zapamiętaj, że każda instrukcja w Delphi musi być zakończona znakiem średnika (;). Jest to informacja dla
kompilatora, że w tym miejscu kończy się jedna instrukcja. Znak średnika jako symbol zakończenia instrukcji
obowiązuje w większości języków programowania (Java, C/C++, Delphi, PHP).
Oczywiście istnieją pewne wyjątki od tej reguły. Na samym początku tego rozdziału zaprezentowałem Ci
najprostszy program, który zakończony był znakiem kropki, a nie średnika!
Bloki begin i end
Właściwy kod programu zawsze wpisywany jest pomiędzy instrukcje begin i end.
Słowo end może oznaczać zarówno zakończenie jakiegoś bloku instrukcji, jak i zakończenie programu. W
pierwszym przypadku na końcu tego słowa stawiamy znak średnika, a w drugim przypadku znak kropki.
Program podczas uruchamiania zawsze czyta instrukcje rozpoczynające się od słowa begin jest to jakby
początek programu i właściwych poleceń, które mają być wykonane po starcie aplikacji.
Pamiętaj, aby ilość instrukcji begin była równa ilości instrukcji end w przeciwnym razie kompilator wyświetli
błąd:
[Error] dprMin.dpr(9): 'END' expected but end of file found.
Taki kod jest jak najbardziej prawidłowy:
begin
begin
begin
end;
end;
end.
Natomiast brak jednego ze słów end spowoduje wyżej wspomniany błąd. Jeżeli natomiast zabraknie jednego ze
słów begin, Delphi wyświetli błąd: [Error] dprMin.dpr(10): '.' expected but ';' found.
Dyrektywa program
Typowy program powinien składać się z głównej dyrektywy program oraz słów begin i end. Co prawda
najprostszy program to jedynie słowo end, ale w prawidłowo zaprojektowanej aplikacji powinien znajdować się
także nagłówek program, identyfikujący jej nazwę.
Po utworzeniu projektu dyrektywa program zawiera jego nazwę. Przykładowo jeżeli plik główny projektu nosi
nazwÄ™ project.dpr, to pierwszy wiersz owego pliku wyglÄ…da tak:
program project;
Dyrektywa powinna być oczywiście zakończona znakiem średnika.
Komentarze
Komentarze są chyba najprostszym elementem każdego języka programowania. Jest to blok tekstu, który nie
jest interpretowany przez kompilator. W komentarzach możesz zawrzeć swoje przemyślenia oraz uwagi
dotyczące kodu zródłowego.
program project;
begin
{ to jest komentarz }
end.
Typowy komentarz Delphi zawarty jest pomiędzy znakami {}. W edytorze kodu komentarz jest wyróżniony
kursywÄ… i kolorem ciemnoniebieskim.
Istnieje kilka typów komentarzy np. komentarz jednowierszowy:
// to jest komentarz jednowierszowy
Wiele osób ten rodzaj komentarzy nazywa komentarzami w stylu C, gdyż został on zapożyczony z języka C. Jak
sama nazwa wskazuje, komentarzem jest tylko jeden wiersz kodu zródłowego:
program project;
begin
// tu jest komentarz
3 z 32 2009-03-14 15:25
Delphi :: Kompendium :: Rozdział 2 - 4programmers.net http://4programmers.net/Delphi/Kompendium/Rozdzia%C5%82_2
a tu już nie ma komentarza
end.
Drugi wiersz w bloku begin nie jest komentarzem podczas kompilacji Delphi wskaże błąd.
Istnieje jeszcze jeden typ komentarzy najrzadziej używany przez programistów:
program project;
begin
(*
komentowany kod
*)
end.
Zaletą tego typu komentarza jest to, że może on w sobie zawierać również znaki {.
program project;
begin
(*
komentarz...
{ jakiÅ› tekst }
*)
end.
Jak widzisz, taki sposób pozwala na umieszczanie komentarzy w komentarzu, ale zapewne rzadko będziesz z
niego korzystał.
Zmienne
Czym byłby program, który nie mógłby zapisywać danych do pamięci komputera Praktycznie każdy program
podczas działania korzysta z pamięci, aby przechować różne dane, potrzebne do dalszego jego działania.
Zmienne to obszar w pamięci komputera, który służy do przechowywania danych
tymczasowych (obecnych w pamięci do czasu wyłączenia programu), mających postać liczb,
tekstu itp.
Deklaracja zmiennych
Przed przydzieleniem danych do pamięci zmienną należy zadeklarować w kodzie programu. Deklaracja
zmiennej powinna być umieszczona przed blokiem begin. Przykładowa deklaracja może wyglądać tak:
program varApp;
var
Zmienna : String;
begin
end.
W razie potrzeby zadeklarowania zmiennej konieczne jest zastosowanie słowa kluczowego var (skrót od słowa
variable zmienna). Stanowi to informację dla kompilatora, że po tym słowie kluczowym zostanie
umieszczona deklaracja zmiennych.
Zmienna zawsze musi mieć nazwę! Dzięki tej nazwie możemy łatwo odwołać się do poszczególnych danych
zapisanych w pamięci. Pierwszym członem deklaracji zmiennej musi być unikalna nazwa (nie mogą istnieć dwie
takie same zmienne w programie). Po znaku dwukropka należy wpisać typ zmiennej (o typach zmiennych
powiem pózniej).
Z punktu widzenia kompilatora nie ma znaczenia, w jaki sposób zapiszesz (zadeklarujesz) zmienną może
więc odbyć się to tak:
var
zmienna:string;
lub tak:
var zmienna: string;
Dla zachowania przejrzystości kodu zalecane jest jednak stosowanie deklaracji w formie przedstawionej w
pierwszym przykładzie.
Typy zmiennych
Typy zmiennych określają rodzaj danych, który będzie zapisywany w pamięci. W poprzednim podpunkcie
podczas deklarowania zmiennej skorzystałem z typu String. Ten typ danych służy do przechowywania tekstu i
tylko tekstu! Tak więc chcąc w pamięci komputera umieścić np. liczbę, należy skorzystać z innego typu
zmiennej.
Typy zmiennych przedstawiłem w tabeli 2.1. Oczywiście tych typów jest więcej, ale nie musisz znać ich
wszystkich wystarczÄ… te podstawowe.
Tabela 2.1. Typy zmiennych w Object Pascalu
Nazwa Opis
Integer -2 147 483 648 2 147 483 647
4 z 32 2009-03-14 15:25
Delphi :: Kompendium :: Rozdział 2 - 4programmers.net http://4programmers.net/Delphi/Kompendium/Rozdzia%C5%82_2
Int64 -263 263 - 1
SmallInt -32 768 32 767
ShortInt -128 127
Byte 0 255
Word 0 65 535
LongWord 0 4 294 967 295
Char pojedynczy znak
Boolean TRUE lub FALSE
ShortString 255 znaków
AnsiString
231 znaków
String
231 znaków
Extended
3,6 × 10-4951 1,1 × 104932
Double
5,0 × 10-324 1,7 × 10308
Single
1,5 × 10-45 3,4 × 1038
Currency -922 337 203 685 477,5808 922 337 203 685 477,5807
Niektóre z tych typów służą do przechowywania tekstu, inne do przechowywania liczb. Różni je pojemność .
Przykładowo chcąc zapisać w pamięci jakąś dużą liczbę, nie skorzystasz z typu Byte, ponieważ do tego typu
mogą być przypisywane jedynie wartości z zakresu od 0 do 255. Możesz za to skorzystać z typu Int64.
Oprócz, jak to wcześniej nazwałem, pojemności powyższe typy danych różni także ilość zajmowanej pamięci
operacyjnej. Przykładowo typ Byte zżera jedynie 1 bajt pamięci, a typ Int64 8 bajtów. Możesz pomyśleć,
że to nieduża różnica, ale jeśli zmiennych 8 bajtowych jest kilkadziesiąt (kilkaset?) Jest to zwykłe
marnowanie pamięci!
Podczas czytania tej książki oraz podczas przeglądania różnych kodów zródłowych możesz
zauważyć, że dla typów liczbowych programiści często stosują zmienną Integer. Jest to
jakby uniwersalny typ zmiennej liczbowej, gdyż jej zakres jest w miarę duży, a nie
wykorzystuje ona aż tak dużo pamięci.
Deklaracja kilku zmiennych
Po wpisaniu słowa kluczowego var możesz zadeklarować tyle zmiennych, ile będzie Ci potrzebne nie musisz
za każdym razem używać dyrektywy var.
program varApp;
var
Zmienna1 : String;
Zmienna2 : String;
Zmienna3 : String;
begin
end.
W powyższym przypadku zadeklarowałeś trzy zmienne typu String. Od tej pory dla kompilatora słowa
Zmienna1, Zmienna2, Zmienna3 nie są już konstrukcjami nieznanymi wiadome będzie, że w tym przypadku
chodzi o zmienne.
Podczas deklaracji kilku zmiennych tego samego typu można wpisać wszystkie zmienne razem, oddzielając ich
nazwy przecinkami:
program varApp;
var
Zmienna1, Zmienna2, Zmienna3 : String;
begin
end.
Z punktu widzenia kompilatora w tym wypadku również następuje deklaracja trzech zmiennych typu String.
Chcąc jeszcze zadeklarować zmienne innego typu, należy to zrobić w ten sposób:
program varApp;
var
Zmienna1, Zmienna2, Zmienna3 : String;
Liczba1, Liczba2 : Integer;
begin
end.
Przydział danych
Przydzielenie danych dla zmiennej musi odbyć się w bloku begin. Istnieje jednak możliwość przydzielenia
danych w trakcie pisania programu.
Przydział statyczny
5 z 32 2009-03-14 15:25
Delphi :: Kompendium :: Rozdział 2 - 4programmers.net http://4programmers.net/Delphi/Kompendium/Rozdzia%C5%82_2
~~~~~~~~~~~~
W celu określenia wartości dla konkretnej zmiennej należy to zrobić podczas jej deklarowania, używając w tym
celu znaku równości (=).
program varApp;
var
Zmienna1 : String = 'Oto zmienna nr 1';
begin
end.
Taki kod spowoduje, że na samym starcie programu zmienna Zmienna1 będzie miała wartość Oto zmienna nr
1.
Każdy tekst zadeklarowany w ramach zmiennej musi być ujęty w znaki apostrofów.
Podczas pisania programu nie możesz przydzielić wartości kilku zmiennym naraz:
program varApp;
var
Zmienna1, Zmienna2 : String = 'Oto zmienna nr 1';
begin
end.
Próba uruchomienia takiego programu spowoduje wyświetlenie błędu: [Error] varApp.dpr(4): Cannot initialize
multiple variables.
program varApp;
var
Zmienna1 : String = 'Oto zmienna nr 1';
Zmienna2 : String = 'Oto zmienna nr 2';
begin
end.
Natomiast kod przedstawiony powyżej będzie już całkiem prawidłowy.
Przydział wartości dla zmiennej podczas pisania kodu często nazywany jest przydziałem
domyślnym.
Jeżeli spróbujesz uruchomić program, a kompilator znajdzie zmienną, której nie przypisałeś
żadnej wartości, zostanie wyświetlone ostrzeżenie: [Hint] varApp.dpr(4): Variable
'Zmienna1' is declared but never used in 'varApp'.
Przydział dynamiczny
Możliwa jest także zmiana zawartości danej zmiennej podczas pracy programu. Jest to czynność stosunkowo
prosta polega ona na zastosowaniu znaku :=, tzw. operatora przydziału. Oto przykład:
program varApp;
var
Zmienna1 : String;
Zmienna2 : String;
begin
Zmienna1 := 'Oto jest zmienna nr 1';
Zmienna2 := 'Oto jest zmienna nr 2';
end.
Oczywiście nic się nie stanie jeżeli ponownie zmienisz wartość już raz deklarowanej zmiennej.
Stałe
Podobnie jak zmienne, stałe również służą do przechowywania jakichś danych podczas działania aplikacji. Jest
jednak pomiędzy nimi jedna różnica stałe, jak sama nazwa wskazuje, nie mogą podlegać modyfikacji
podczas działania programu. Czyli wartość stałych jest określana już podczas pisania programu:
program varConst;
const
Stala1 = 'Oto jest stała...';
begin
end.
Stałe, w odróżnieniu od zmiennych, deklarujemy z użyciem słowa kluczowego const. Jak widzisz, nie
deklarujemy także typu zmiennej typ jest określany automatycznie na podstawie wartości.
Domyślne typy stałych
Jeżeli przykładowo przypisujesz stałej jakąś liczbę:
6 z 32 2009-03-14 15:25
Delphi :: Kompendium :: Rozdział 2 - 4programmers.net http://4programmers.net/Delphi/Kompendium/Rozdzia%C5%82_2
const
Stala2 = 12;
Delphi uzna, że stała jest stałą typu Integer (jest to domyślny typ stałych). Programista może to w dość prosty
sposób zmienić:
program varConst;
const
Stala1 = 'Oto jest stała...';
Stala2 : Byte = 12;
begin
end.
A zatem w tym przypadku Stala2 będzie stałą typu Byte o wartości 12.
Jeżeli spróbujesz przypisać jakąś wartość stałej przykładowo:
begin
Stala1 := 'Inna wartość';
end.
Delphi uzna to za błąd i wyświetli podpowiedz dla Ciebie: [Error] varConst.dpr(8): Left side cannot be assigned
to.
Używanie stałych i zmiennych w programie
Jeżeli potrafisz już deklarować stałe i zmienne, należy z nich wreszcie skorzystać. Przy tej okazji poznasz
pierwsze polecenie ShowMessage. Jego użycie spowoduje wyświetlenie okienka informacyjnego. Z polecenia
tego korzysta się w następujący sposób:
program varConst;
uses Dialogs; // włączanie modułu do programu tym zajmiemy się pózniej
begin
ShowMessage('To jest tekst w okienku!');
end.
Program rozpoczynamy pewnym słowem kluczowym uses. Nie zawracaj sobie jednak tym głowy zajmiemy
się tą kwestią w dalszej części rozdziału. Najważniejsze jest polecenie ShowMessage. W nawiasie oraz w
apostrofach wpisujemy tekst, który ma być wyświetlony w oknie. Uruchom teraz program, naciskając klawisz F9
rezultat działania przedstawiony jest na rysunku 2.1.
Rysunek 2.1. Rezultat działania programu
Zamiast tekstu w apostrofach możesz wpisać nazwę zmiennej tym sposobem program podczas działania
podstawi na to miejsce zawartość zmiennej. Tak samo ma się sprawa ze stałymi oto przykład:
program varConst;
uses Dialogs; // włączanie modułu do programu tym zajmiemy się pózniej
const
Stala1 = 'Oto jest stała...';
var
Zmienna1 : String;
begin
Zmienna1 := 'Tekst umieszczony w okienku!';
ShowMessage(Zmienna1);
ShowMessage(Stala1);
end.
Na samym początku w bloku begin następuje przypisanie danych zmiennej, a kolejnym krokiem jest
wyświetlenie jej zawartości; następne okienko wyświetli natomiast zawartość stałej.
Tablice danych
Wyobraz sobie przypadek, gdy w programie musisz użyć wielu, naprawdę wielu zmiennych. Czy wygodne jest
w takim przypadku deklarowanie tylu zmiennych, z inną nazwą dla każdej Do tego właśnie służą tablice.
Tablice deklarowane są za pomocą słowa kluczowego array.
program arrayApp;
uses
Dialogs;
var
Tablica : array[0..1] of String;
begin
end.
7 z 32 2009-03-14 15:25
Delphi :: Kompendium :: Rozdział 2 - 4programmers.net http://4programmers.net/Delphi/Kompendium/Rozdzia%C5%82_2
Konstrukcja tablic jest dość specyficzna. Po słowie kluczowym array w nawiasach kwadratowych należy wpisać
ilość elementów, z których składać się będzie tablica.
Nazwa_Tablicy : array[Najmniejszy_Indeks..Największy_Indeks] of Typ_danych;
W powyższym przypadku najmniejszym indeksem jest 0, a największym 1. Z tego powodu tablica składać
się będzie z dwóch elementów (zero jest także liczone jako jeden element).
Przydział danych odbywa się także z zastosowaniem nawiasów kwadratowych:
program arrayApp;
var
Tablica : array[0..1] of String;
begin
Tablica[0] := 'Pierwszy element tablicy';
Tablica[1] := 'Drugi element tablicy';
end.
Podsumujmy: z tablic korzysta się tak samo jak ze zmiennych. Jedyną różnicą jest to, że należy zawsze
podawać numer indeksu, do którego chce się zapisać lub odczytać dane.
Tablice jako stałe
Możliwe jest deklarowanie tablic jako stałych. Tak, jak w przypadku zwykłych stałych, dane także należy
przypisać tablicy podczas projektowania aplikacji:
program arrayConst;
const
Tablica : array[0..1] of String = (
('Pierwszy element'), ('Drugi element')
);
begin
end.
Także w tym przykładzie tablica składa się z dwóch elementów. Dodatkowe nawiasy zostały wprowadzone
jedynie po to, aby zwiększyć czytelność równie dobrze można by zapisać program w ten sposób:
program arrayConst;
const
Tablica : array[0..1] of String = (
'Pierwszy element', 'Drugi element');
begin
end.
Obowiązkowy jest jedynie jeden nawias, w którym wypisujemy elementy tablicy, oddzielając je przecinkami.
Należy uważać na przydział danych zgodnie z ilością elementów, jakie zostały zadeklarowane w kodzie.
Przykładowy kod:
program arrayConst;
const
Tablica : array[0..2] of String = (
'Pierwszy element', 'Drugi element');
begin
end.
nie będzie mógł zostać skompilowany zadeklarowano trzy elementy, a dane przydzielono jedynie do dwóch.
Delphi wyświetli błąd: [Error] arrayConst.dpr(5): Number of elements differs from declaration.
Tablice wielowymiarowe
Object Pascal umożliwia także deklarowanie tablic tzw. wielowymiarowych. Po zadeklarowaniu takich tablic do
konkretnego elementu możemy odwołać się w następujący sposób:
Tablica[0][0] := 'Przypisanie danych';
W powyższym przypadku skorzystałem jedynie z tablic dwuwymiarowych, których deklaracja wygląda tak:
var
Tablica : array[0..1, 0..1] of String;
Deklaracja jest także specyficzna polega bowiem na wypisywaniu ilości elementów w nawiasie kwadratowym,
przy czym poszczególne elementy są oddzielone przecinkami. W przedstawionej wyżej deklaracji mamy aż 4
elementy! Przydział danych odbywa się w następujący sposób:
program arrayarray;
var
Tablica : array[0..1, 0..1] of String;
begin
8 z 32 2009-03-14 15:25
Delphi :: Kompendium :: Rozdział 2 - 4programmers.net http://4programmers.net/Delphi/Kompendium/Rozdzia%C5%82_2
Tablica[0][0] := 'Element 1';
Tablica[0][1] := 'Element 2';
Tablica[1][0] := 'Element 3';
Tablica[1][1] := 'Element 4';
end.
Istotę działania tablic dwuwymiarowych lepiej zrozumiesz, przeglądając listing 2.2.
Listing 2.2. Program deklarujÄ…cy dwuwymiarowe tablice
program arrayarray;
var
Tablica : array[0..1, 0..2] of String;
begin
Tablica[0][0] := 'Fiat';
{ marka samochodu }
Tablica[0][1] := 'Uno';
Tablica[0][2] := 'Punto';
{ modele samochodu }
Tablica[1][0] := 'Audi';
Tablica[1][1] := 'A4';
Tablica[1][2] := 'A8';
end.
W tym przypadku nastąpiła deklaracja tablicy 2x3. Dwa główne elementy to element Fiat oraz element Audi.
Kolejne dwa podpola określają modele samochodów.
Przedstawiając tablice wielowymiarowe, mówiłem tylko o dwóch wymiarach. Istnieje jednak możliwość
zadeklarowania tablic, które będą miały wiele wymiarów.
program arrayx3;
var
Tablica : array[0..1, 0..1, 0..1] of String;
begin
Tablica[0][0][0] := 'Wartość';
{ itd. }
end.
W tym przykładzie nasza tablica to tablica 3x2 typu String. W jaki sposób dane są przydzielane do tej tablicy
Przykład znajduje się w powyższym kodzie zródłowym.
Tablice dynamiczne
Nieraz podczas pracy z Delphi w programie wymagane będzie zadeklarowania tablicy o niewiadomej liczbie
elementów. Znaczy to, że programista w trakcie pisania programu nie jest w stanie określić, ile elementów
tablicy będzie mu potrzebne. W tym celu w Delphi 4 zaimplementowano możliwość tworzenia tablic
dynamicznych. Tablice dynamiczne deklaruje się bez podania ilości elementów:
program dynArray;
var
Tablica : array of String;
begin
end.
Przy tej okazji poznasz nowe polecenie SetLength. Służy ono do określenia ilości elementów tablicy podczas
działania programu. Pierwszym parametrem tego polecenia jest nazwa tablicy dynamicznej drugi parametr
to ilość elementów, z których tablica ma się składać. Parametry przekazywane do polecenia muszą być
oddzielane przecinkami:
program dynArray;
var
Tablica : array of String;
begin
SetLength(Tablica, 2);
end.
Od tej pory po uruchomieniu programu tablica składać się będzie z dwóch elementów. Wypełnianie elementów
danymi odbywa się tak samo jak w przypadku zwykłych tablic:
program dynArray;
var
Tablica : array of String;
begin
SetLength(Tablica, 2);
Tablica[0] := 'Wartość 1';
Tablica[1] := 'Wartość 2';
end.
Na poziomie tworzenia programu nie jest możliwe określenie przez kompilator, z ilu elementów ostatecznie
będzie składać się tablica. Stąd próba odczytu elementu spoza tablicy może skończyć się komunikatem o
błędzie!
9 z 32 2009-03-14 15:25
Delphi :: Kompendium :: Rozdział 2 - 4programmers.net http://4programmers.net/Delphi/Kompendium/Rozdzia%C5%82_2
Polecenia Low i High
Oba polecenia Low i High mogą być użyte jedynie w połączeniu z tablicami. Warto je znać, gdyż czasem
mogą się przydać. Zwracają one liczbę równą najmniejszemu (Low) oraz największemu (High) indeksowi
tablicy.
Deklaracja tablicy może na przykład wyglądać w ten sposób:
Tablica : array[10..100] of Integer;
Wywołanie polecenia Low(Tablica) spowoduje, że funkcja zwróci wartość 10. Natomiast funkcja High zwróci
wartość 100.
Operatory
W języku Object Pascal istnieje wiele operatorów. Dwa z nich zastosowałeś już wcześniej. Są to operatory
przypisania (:=) i porównania (=). Operator porównania, jak zapewne zauważyłeś, jest także używany do
przydzielania zmiennym i stałym domyślnych wartości.
Najprościej mówiąc, operatory to elementy (znaki) języka służące do manipulowania danymi. Istnieją operatory
porównania, arytmetyczne i przypisania. Większość z nich zaprezentowałem w tabeli 2.2.
Operator Znaczenie
= Porównanie sprawdza, czy obie wartości są sobie równe
:= Przypisanie danych jeden z najważniejszych operatorów
<> Różne od sprawdza, czy obie wartości są od siebie różne
> Większe od sprawdza, czy jedna wartość z podanych zmiennych jest większa od drugiej
> Mniejsze od sprawdza, czy jedna wartość jest mniejsza od drugiej
>= Większe lub równe
<= Mniejsze lub równe
+ Dodawanie
- Odejmowanie
* Mnożenie
/ Dzielenie
div Dzielenie z obcięciem reszty
mod Dzielenie z zachowaniem reszty z dzielenia
and Porównywanie typów logiczne i
or Porównywanie typów logiczne lub
not Zaprzeczenie (stosowane podczas porównywania typów)
xor Operator bitowy dysjunkcja
shl Operator bitowy przesunięcie w lewo
shr Operator bitowy przesunięcie w prawo
Zastosowanie większości z tych operatorów poznasz w kolejnym podpunkcie Instrukcje warunkowe . Oprócz
wyżej podanych operatorów istnieją także funkcje Inc oraz Dec. Funkcje te służą odpowiednio do zwiększania i
zmniejszania wartości parametru. Są one równoważne następującym instrukcjom:
X := X + 1; // to samo co Inc(x)
Y := Y 1; // to samo co Dec(Y)
Ponadto funkcje Inc i Dec posiadają opcjonalny parametr dodatkowy. Domyślnie bowiem wartość podanej
zmiennej jest zwiększana lub zmniejszana o jeden. Drugi parametr decyduje o tym, czy ma to być wartość
większa niż jeden np.
var
I : Integer;
begin
I := 1; // przydzielenie wartości początkowej
Inc(I, 2); // w tym momencie zmienna I posiada wartość 3
end.
Aplikacje konsolowe
W dalszej części tego rozdziału korzystać będziemy z tzw. aplikacji konsolowych. Oczywiście będziemy się tym
zajmować tylko w tym rozdziale podczas czytania kolejnych korzystać już będziesz z głównych funkcji, jakie
oferuje Delphi, czyli komponentów i formularzy. Na razie jednak, aby wszystko lepiej zrozumieć, proponuję Ci
wykorzystanie w przykładach aplikacji konsolowych. Inaczej mówiąc, aplikacje konsolowe to programy, których
rezultat wyświetlany jest w oknie trybu MS-DOS.
Aby program mógł działać w trybie konsolowym, nie trzeba wykonywać żadnych skomplikowanych czynności
wystarczy dodać do projektu taką oto dyrektywę:
{$APPTYPE CONSOLE}
Dyrektywa ta zawarta jest jednak w nawiasie klamrowym, czyli teoretycznie powinna być traktowana przez
kompilator jako komentarz. Tak jednak nie jest, a to za sprawą znaku $ na samym początku. Przykładowy
program może zatem wyglądać następująco:
10 z 32 2009-03-14 15:25
Delphi :: Kompendium :: Rozdział 2 - 4programmers.net http://4programmers.net/Delphi/Kompendium/Rozdzia%C5%82_2
program appconsole;
{$APPTYPE CONSOLE}
begin
Readln;
end.
Instrukcja Readln w bloku programu nakazuje czekanie na naciśnięcie przez użytkownika klawisza Enter.
Dopiero po wykonaniu tej czynności program zostanie zamknięty.
Wpisywanie tekstu do okna konsoli odbywa siÄ™ poprzez zastosowanie polecenia Writeln lub Write. W tym
pierwszym na końcu dodawany zostaje znak nowej linii, natomiast w drugim nie.
program appconsole;
{$APPTYPE CONSOLE}
begin
Write('Linia, która nie będzie zakończona znakiem nowej linii');
Writeln('To linia, która będzie zakończona znakiem nowej linii');
Write('A ta znowu nie będzie...');
Readln;
end.
Uruchom program przedstawiony w powyższym listingu, aby sprawdzić rezultat działania.
Instrukcje warunkowe
Instrukcje warunkowe są elementem programowania obecnym w każdym języku. Są bardzo często
wykorzystywane, więc niezbędne jest poznanie ich i zrozumienie zasad ich działania. Instrukcje te służą
bowiem do porównywania danych i wykonywania na nich różnych operacji. Wykorzystując instrukcje
warunkowe, jesteś w stanie odpowiednio zareagować na daną sytuację.
Instrukcja if..then
Podstawową instrukcją warunkową jest instrukcja if..then. Jej budowa jest następująca:
if { tu następuje sprawdzanie warunku } then { wykonaj pewną operację }
Po słowie if musi się znalezć pewien warunek do sprawdzenia przykładowo
if 4 > 3 then
Taki warunek zostanie zawsze spełniony, gdyż wiadomo, że cyfra 4 jest większa od 3. Tak więc za każdym
razem zostanie wykonany kod umieszczony po słowie then.
program if_then;
{$APPTYPE CONSOLE}
begin
if 4 > 3 then
Writeln('Tak... warunek został spełniony!');
Readln;
end.
Po uruchomieniu powyższego programu za każdym razem wyświetlony zostanie tekst Tak... Warunek został
spełniony!.
Pobieranie tekstu z konsoli
Przeprowadzmy małe ćwiczenie. Otóż po uruchomieniu programu zostaniemy poproszeni o wpisanie pewnego
tekstu imienia. Jeśli wpisane zostanie imię Adam, zostaną wykonane pewne czynności; w przeciwnym
wypadku zostanie wyświetlony tekst powitalny. Kod programu realizującego to zadanie ma następującą postać:
program if_then;
{$APPTYPE CONSOLE}
var
Imie : String;
begin
Writeln('Wpisz swoje imiÄ™...');
Readln(Imie); // pobranie wpisanej wartości
if Imie = 'Adam' then
Writeln('Super! Ja też mam na imię Adam!');
if Imie <> 'Adam' then
Writeln('Cześć ' + Imie);
Readln;
end.
Na samym początku programu pobierane są dane wpisane przez użytkownika programu. Realizuje to procedura
Readln. Za jej pomocą dane wpisane przez użytkownika wędrują do zmiennej Imie. Cel naszego ćwiczenia to
sprawdzenie, czy wpisany tekst to Adam.
Zwróć także uwagę na ten wiersz kodu:
11 z 32 2009-03-14 15:25
Delphi :: Kompendium :: Rozdział 2 - 4programmers.net http://4programmers.net/Delphi/Kompendium/Rozdzia%C5%82_2
Writeln('Cześć ' + Imie);
Do wyświetlanego napisu Cześć dołączana jest także zawartość zmiennej Imie. Jak widzisz, operator (+) ma
także takie zadanie łączenie dwóch tekstów.
Kilka instrukcji po słowie then
Istnieje pewna zasada, którą należy zapamiętać i która będzie wykorzystywana w dalszej części tego rozdziału.
Otóż wiele instrukcji po słowie then musi być umieszczone dodatkowo w bloku tekstu między begin a end
(przykład: listing 2.3.)
Listing 2.3. Pobieranie danych z konsoli i porównywanie ich za pomocą operatora if
program if_then;
{$APPTYPE CONSOLE}
var
Imie : String;
begin
Writeln('Wpisz swoje imiÄ™...');
Readln(Imie); // pobranie wpisanej wartości
if Imie = 'Adam' then
begin // dodatkowa instrukcja begin
Writeln('Super! Ja też mam na imię Adam!');
Writeln('Hahhahaa...');
end;
Readln;
end.
W całym powyższym listingu interesuje nas jedynie ten fragment kodu:
if Imie = 'Adam' then
begin // dodatkowa instrukcja begin
Writeln('Super! Ja też mam na imię Adam!');
Writeln('Hahhahaa....');
end;
Oznacza on, że gdy wartość zmiennej jest równa Adam, wykonane będą w wyniku tego dwie instrukcje Writeln.
Taki wypadek zmusza nas do umieszczenia dodatkowo słów kluczowych begin oraz end. Jeżeli blok begin..end
nie znalazłby się w kodzie, to druga instrukcja Writeln wykonana zostałaby niezależnie od tego, czy zmienna
Imie posiadałaby wartość Adam, czy też nie.
Kilka warunków do spełnienia
To, czy dany kod zostanie wykonany, może zależeć od wielu czynników. Niekoniecznie musi być to jeden
warunek do spełnienia może być ich wiele. Jednak konieczne jest umieszczenie wszystkich tych warunków w
nawiasie (listing 2.4.).
Listing 2.4. Kilka warunków w instrukcji if
program if_then;
{$APPTYPE CONSOLE}
var
Imie : String;
begin
Writeln('Wpisz swoje imiÄ™...');
Readln(Imie); // pobranie wpisanej wartości
Randomize; // procedura losujÄ…ca
if (Imie = 'Adam') and (Random(10) = 5) then
Writeln('Super! Ja też mam na imię Adam!');
Readln;
end.
Kod ten można przetłumaczyć następująco: jeżeli zmienna Imie zawiera wartość Adam i rezultat losowania
spośród 10 liczb wynosi 5, wyświetl tekst na konsoli . Jak widzisz, aby warunek został spełniony pomyślnie,
muszą zostać wykonane dwie czynności.
Wielu początkujących programistów często orientuje się, że zapomnieli zastosować nawiasów podczas
sprawdzania kilku warunków. Spowoduje to w tym wypadku wyświetlenie błędu: [Error] if..then.dpr(14):
Incompatible types: 'String' and 'Integer'.
Poznałeś przy tej okazji znaczenie kolejnej procedury, a mianowicie Random. Polecenie to
realizuje proces losowania spośród liczb, z których najwyższa podana jest w nawiasie. A
zatem wywołanie procedury w ten sposób Random(100) spowoduje wylosowanie liczby
z zakresu od 0 do 99. Tak! Nie pomyliłem się! Możliwe jest, że wyniku losowania zwrócona
zostanie wartość 0.
Pamiętaj, aby zawsze przed wywołaniem procedury Random umieszczać w kodzie instrukcję Randomize.
Powoduje ona zainicjowanie procesu losowania.
Instrukcja case..of
Drugą instrukcją warunkową jest case..of. Instrukcja ta również realizuje proces porównywania danych.
Często stosowana jest w przypadku, gdy mamy do sprawdzenia wiele warunków instrukcja if..then nie
zdaje wówczas egzaminu, gdyż należałoby porównać wiele wartości ze sobą. Idealnie za to nadaje się do tego
instrukcja case..of; jej składnia jest następująca:
case Nazwa_Zmiennej of
12 z 32 2009-03-14 15:25
Delphi :: Kompendium :: Rozdział 2 - 4programmers.net http://4programmers.net/Delphi/Kompendium/Rozdzia%C5%82_2
0: { instrukcje wykonywane w przypadku, gdy zmienna ma wartość 0 }
1: { instrukcje wykonywane w przypadku, gdy zmienna ma wartość 1 }
end;
Jak widać, schemat instrukcji jest raczej prosty. Nie zapomnij o słowie end;, kończącym instrukcję warunkową.
Przykładowy program zaprezentowałem w listingu 2.5.
Listing 2.5. Program wykorzystujÄ…cy instrukcjÄ™ case..of
program case_of;
{$APPTYPE CONSOLE}
var
Mandat : Integer;
begin
Mandat := 50;
case Mandat of
10: Writeln('E, 10 zł. Może jeszcze zapłacę?');
20: Writeln('20 zł nie będę miał na chleb!');
50: Writeln('50 zł Panie władzo... może dogadamy się w inny sposób?');
end;
Readln;
end.
Po uruchomieniu programu wykonany zostanie warunek ostatni, gdyż zmiennej Mandat nadałem na sztywno
wartość 50; chciałem jednak tylko zaprezentować sposób użycia instrukcji case..of.
Zakresy
Dużą zaletą instrukcji case..of jest możliwość sprawdzania wartości od-do . Przykładowo jeżeli wartość
zmiennej Mandat wynosi między 10 a 15, wykonany zostanie konkretny warunek. Zmodyfikujemy poprzedni
program do takiej postaci, aby wartość zmiennej była losowana:
program case_of;
{$APPTYPE CONSOLE}
var
Mandat : Integer;
begin
Randomize;
Mandat := Random(50)+1; // dodajemy 1, aby nie zostało wylosowane 0
case Mandat of
1..10: Writeln('E, 10 zł. Może jeszcze zapłacę?');
11..20: Writeln('20 zł ? nie będę miał na chleb!');
21..50: Writeln('50 zł ? Panie władzo... może dogadamy się w inny sposób?');
end;
Readln;
end.
Dzięki zastosowaniu zakresów pierwsza instrukcja zostanie zrealizowana w przypadku, gdy zmienna Mandat
będzie miała wartość od 1 do 10.
Brak możliwości korzystania z łańcuchów
Wadą instrukcji case..of jest brak możliwości porównywania danych tekstowych. Najlepiej sprawdzić to na
przykładzie spójrz na poniższy kod:
program case_strings;
{$APPTYPE CONSOLE}
var
S : String;
begin
S := 'HÄ™';
case S of
'HÄ™': Writeln('???');
end;
Readln;
end.
Próbujemy tu sprawdzić, czy zmienna S ma wartość Hę. Niestety próba kompilacji takiego programu zakończy
się błędem: [Error] case_strings.dpr(11): Ordinal type required.
Kilka instrukcji
Tak samo, jak w przypadku instrukcji warunkowej if..then, instrukcja case..of wymaga umieszczenia kodu
w bloku begin..end w przypadku, gdy kod ten zawiera więcej niż jedną instrukcję.
program case_of;
{$APPTYPE CONSOLE}
var
Mandat : Integer;
begin
13 z 32 2009-03-14 15:25
Delphi :: Kompendium :: Rozdział 2 - 4programmers.net http://4programmers.net/Delphi/Kompendium/Rozdzia%C5%82_2
Randomize;
Mandat := Random(50)+1; // dodajemy 1, aby nie zostało wylosowane 0
case Mandat of
1..10: Writeln('E, 10 zł. może jeszcze zapłacę');
11..20: Writeln('20 zł. nie będę miał na chleb');
21..50:
begin
Writeln('50 zł. Panie władzo... może dogadamy się w inny sposób?');
Writeln('Nieeeee...');
end;
end;
Readln;
end.
Sam widzisz jeżeli wylosowana zostanie wartość pomiędzy 21 a 50, wykonane zostaną dwie instrukcje. W
takim przypadku należy umieścić kod w dodatkowym bloku begin..end.
Instrukcja else
Angielskie słowo else można w tym kontekście przetłumaczyć jako w przeciwnym wypadku. Konstrukcja else
jest zawsze stosowana w połączeniu z instrukcją if..then oraz case..of. Za jej pomocą można wykonać takie
operacje, które nie mieszczą się w ramach wcześniej wykorzystywanych instrukcji. Oczywiście wszystko
najlepiej zrozumieć na przykładach napisz zatem przykładowy program. Pamiętasz pierwszy program, jaki
zaprezentowałem podczas omawiania instrukcji if Zmodyfikuj go do takiej postaci:
program if_then_else;
{$APPTYPE CONSOLE}
var
Imie : String;
begin
Writeln('Wpisz swoje imiÄ™...');
Readln(Imie); // pobranie wpisanej wartości
if Imie = 'Adam' then
Writeln('Super! Ja też mam na imię Adam!') // < uwaga! Brak średnika!
else Writeln('Cześć ' + Imie);
Readln;
end.
W poprzedniej wersji tego programu to, czy zmienna Imie nie zawiera wartości Adam, sprawdzane było w
kolejnym warunku if. Teraz w przypadku, gdy zmienna nie będzie zawierać tej wartości, wyświetlony zostanie
tekst powitalny. Wszystko możliwe jest za sprawą instrukcji else. Mam nadzieję, że ten przykład pomógł Ci
zrozumieć, jak działa instrukcja else.
Zauważ, że w wierszu nad instrukcją else nie ma średnika na końcu! To jest właśnie
wyjątkowa sytuacja, kiedy nie stawiamy średnika!
Kiedy stosować średnik, a kiedy nie ?
Poprzedni przykład z użyciem instrukcji if i else nie zawierał średnika. Kiedy stosować ten średnik, a kiedy nie
Spójrz taki fragment kodu już wymaga postawienie średnika przed else:
if Imie = 'Adam' then
begin
{ jakaÅ› inna instrukcja }
Writeln('Super! Ja też mam na imię Adam!');
end else Writeln('Cześć ' + Imie);
Także w przypadku, gdy po słowie then stosujemy blok begin..end, średnik musi się w kodzie znalezć! Ale
zasada pozostaje taka sama nie stosujemy go przed else!
end else Writeln('Cześć ' + Imie);
Sprawa początkowo może wydać się nieco skomplikowana, ale po dokładniejszym przejrzeniu kodu można
rozróżnić, kiedy należy stosować średnik na końcu, a kiedy nie.
Kilka instrukcji if i else
Możliwe jest połączenie kilku instrukcji if oraz else. Taka konstrukcja ma postać else if { warunek }
Jeżeli jeden warunek nie zostanie spełniony, nastąpi analiza drugiego. Jeżeli także drugi nie zostanie spełniony
analizowane będą kolejne warunki. Spójrz na poniższy kod:
program if_else;
{$APPTYPE CONSOLE}
var
I : Integer;
begin
Randomize;
I := Random(50);
if i = 10 then Writeln('I = 10')
else if i = 20 then Writeln('I = 20')
else if i = 30 then Writeln('I = 30')
{ kod, w razie, gdy żaden warunek nie zostanie spełniony }
14 z 32 2009-03-14 15:25
Delphi :: Kompendium :: Rozdział 2 - 4programmers.net http://4programmers.net/Delphi/Kompendium/Rozdzia%C5%82_2
else Writeln('Żadna wartość nie jest odpowiednia!');
Readln;
end.
Na samym początku następuje sprawdzenie, czy wylosowana liczba to 10; w innym wypadku następuje
sprawdzenie kolejno liczb 20 i 30, co tworzy następne warunki if. Jeżeli żaden z poprzednich warunków nie
zostanie zrealizowany, wykonany zostanie kod umieszczony po słowie else.
Kilka instrukcji po słowie begin
Jak mówiłem wcześniej, często będzie sprawdzać się zasada mówiąca o tym, że gdy po jednym słowie
kluczowym (np. else lub then) wystąpi więcej niż jedna instrukcja, cały fragment kodu należy umieścić w
dodatkowym bloku begin..end.
if i = 10 then Writeln('I = 10')
else if i = 20 then Writeln('I = 20')
else if i = 30 then Writeln('I = 30')
{ kod wykonywany w razie, gdy żaden warunek nie zostanie spełniony }
else
begin
Writeln('Żadna wartość nie jest odpowiednia!');
Writeln('Spróbuj jeszcze raz');
end;
Jak widać w powyższym przykładzie, średnik nie został wstawiony ani po słowie else, ani przed nim.
Instrukcja else w case..of
Możliwe jest także zastosowanie słowa kluczowego else w instrukcji case..of. Jeżeli żaden ze sprawdzanych
warunków case nie zostanie spełniony, można odpowiednio na to zareagować. Posłużę się jednym z
poprzednich przykładów (listing 2.6).
Listing 2.6. Instrukcja else zagnieżdżona w case..of
program case_of_else;
{$APPTYPE CONSOLE}
var
Mandat : Integer;
begin
Randomize;
Mandat := Random(100)+1; // dodajemy 1, aby nie zostało wylosowane 0
case Mandat of
1..10: Writeln('E, 10 zł. Może jeszcze zapłacę?');
11..20: Writeln('20 zł nie będę miał na chleb!');
21..50:
begin
Writeln('50 zł Panie władzo... może dogadamy się w inny sposób?');
Writeln('Nieeeee...');
end;
{ dodajemy else }
else Writeln('JakiÅ› inny mandacik?');
end;
Readln;
end.
Tym razem losowana jest liczba z zakresu od 1 do 100. Dzięki temu instrukcja case może nie uwzględniać tej
liczby wykonany zostanie blok kodu umieszczony po słowie kluczowym else.
Procedury i funkcje
Podczas czytania tego rozdziału mogłeś zauważyć, że nieraz posługiwałem się słowem procedura lub funkcja w
odniesieniu do poleceń języka Object Pascal. Mogłeś odnieść wrażenie, że wszystkie te słowa są synonimami
jednak sprawa wyglÄ…da nieco inaczej.
Procedury
Procedura to wydzielony blok kodu, realizujący określone zadanie.
Procedurą, z której korzystałeś już na samym początku tego rozdziału, jest ShowMessage. Jak pamiętasz,
procedura ta realizowała wyświetlanie okienka informacyjnego. Wyobraz sobie, że w danym programie musisz
często wykonywać ten sam blok kodu np. wyświetlanie kilku tekstów w konsoli. W takim przypadku za
każdym razem należałoby wpisywać po kolei instrukcje Writeln. Dzięki zastosowaniu procedur możesz wpisać te
instrukcje tylko raz pózniej pozostaje już tylko umieszczenie w odpowiednim miejscu nazwy procedury, aby
wykonać kod w niej zawarty. Przykładowa deklaracja procedury wygląda tak:
procedure Nazwa_Procedury;
begin
{ kod procedury }
end;
Jak widzisz, procedurę deklaruje się z użyciem słowa kluczowego procedure, po którym następuje nazwa.
Nazwa procedury musi być unikalna dla każdego programu nie może się powtarzać.
Od teraz za każdym razem, gdy gdzieś w kodzie wpiszesz słowo Nazwa_Procedury, wykonany zostanie kod
umieszczony w jej wnętrzu. Przykładowy program z wykorzystaniem procedur przedstawiam poniżej.
program ProcApp;
15 z 32 2009-03-14 15:25
Delphi :: Kompendium :: Rozdział 2 - 4programmers.net http://4programmers.net/Delphi/Kompendium/Rozdzia%C5%82_2
{$APPTYPE CONSOLE}
procedure Quit;
begin
Writeln('Wyjście z programu! Naciśnij Enter, aby zakończyć!');
Readln;
end;
var
Key : Char;
begin
Writeln('Naciśnij literę Q, aby zakończyć!');
Readln(Key);
if Key = 'Q' then Quit else Writeln('Fajnie, że jesteś z nami');
end.
Pamiętaj o tym, że procedury deklaruje się poza blokiem begin..end! W programie tym zadeklarowałem
procedurę Quit. Od tej pory za każdym razem, gdy wpiszesz w programie słowo Quit, program wyświetli
informację i po naciśnięciu klawisza Enter zakończy swe działanie.
W programie tym zadeklarowałem nowy typ zmiennej, jakiego do tej pory nie używałem. Typ Char, bo to o nim
mowa, umożliwia zapisywanie w pamięci danych o postaci jedynie jednego znaku.
Funkcje
Funkcje są w swoim działaniu bardzo podobne do procedur. Właściwie w innych językach programowania, jak
C/C++ czy Java, procedury w ogóle nie istnieją dostępne są jedynie funkcje. Podstawowa różnica pomiędzy
procedurami a funkcjami polega na tym, że te drugie zwracają jakąś wartość.
Deklaracja funkcji jest bardzo specyficzna, następuje bowiem poprzez użycie słowa kluczowego function.
Oprócz tego funkcja, jak już mówiłem, musi zwracać jakąś wartość po znaku dwukropka należy wpisać typ
zwracanej wartości:
function GetName : String;
begin
end;
Być może w dalszym ciągu nie rozumiesz, w czym funkcje różnią się od procedur najłatwiej napisać
przykładowy program spójrz na listing 2.7
Listing 2.7. Przykładowy program wykorzystujący funkcję
program FuncApp;
{$APPTYPE CONSOLE}
function GetName : String;
begin
Result := 'Adam Boduch';
end;
begin
Writeln('Nazywam siÄ™ ' + GetName);
Readln;
end.
Po uruchomieniu takiego programu na konsoli wyświetlony zostanie napis Nazywam się Adam Boduch. To
wszystko stanie siÄ™ za sprawÄ… wiersza:
Result := 'Adam Boduch';
Słowo Result jest jakby ukrytą zmienną po przypisaniu jej wartości zostanie ona zwrócona przez funkcję.
Spróbuj to samo zrobić z procedurami nie uda Ci się to, ponieważ procedura nie może zwrócić wartości;
program nie zostanie zatem uruchomiony.
Zmienne lokalne
Tak, jak w kodzie programu można zadeklarować zmienne i stałe, tak można je również zadeklarować w ciele
procedury lub funkcji. Takie zmienne nazywa się zmiennymi lokalnymi o określonym czasie życia .
function GetValue : String;
var
S : String;
begin
S := 'Khem...';
Result := S;
end;
Zasada deklarowania zmiennych jest taka sama deklaracja umieszczana jest przed blokiem begin. Zmienne
lub stałe deklarowane w ciele procedury nie są widoczne poza ową procedurą. A zatem w przypadku, gdy
spróbujesz odwołać się do zmiennej umieszczonej w procedurze, Delphi wyświetli komunikat o błędzie.
Dlaczego takie zmienne nazywane są zmiennymi o określonym czasie życia Przyczyną tego jest fakt, że
pamięć dla nich alokowana jest w momencie wywołania procedury, a zwalniania w momencie zakończenia
działania.
Jeżeli mamy program, który korzysta z wielu procedur, zalecane jest używanie o ile to możliwe zmiennych
lokalnych. Dzięki temu oszczędzamy pamięć.
W dalszej części tej książki możesz spotkać się z określeniem zmienne globalne. Takie zmienne to po prostu
zwykłe zmienne, widoczne dla całego programu.
16 z 32 2009-03-14 15:25
Delphi :: Kompendium :: Rozdział 2 - 4programmers.net http://4programmers.net/Delphi/Kompendium/Rozdzia%C5%82_2
To, że zmienne zawarte w ciele procedury nie są widoczne na zewnątrz, nie oznacza, że procedura nie może
używać zmiennych globalnych. Ta zasada działa tylko w jedną stronę!
Parametry procedur i funkcji
Możliwe jest, wraz z wywołaniem danej procedury lub funkcji, przekazanie do niej danych tzw. parametrów.
Objaśnię to na przykładzie wspomnianej wcześniej procedury ShowMessage. Podczas jej wywoływania w
nawiasie należało wpisać wartości, a konkretnie tekst. Ten tekst mógł być przez procedurę wykorzystany do
wyświetlania okna informacyjnego.
Deklaracja procedury lub funkcji zawierającej parametry wygląda następująco:
procedure SetName(Name : String);
begin
{ kod procedury }
end;
Od tej pory chcąc wywołać procedurę SetName, należy podać wartość parametru; parametr musi być typu
String.
Przykładowy program wyświetli na konsoli za pomocą procedury SetName tekst z parametru Name:
program ProcParam;
{$APPTYPE CONSOLE}
procedure SetName(Name : String);
begin
Writeln('Nazywam siÄ™ ' + Name);
end;
begin
SetName('Adam Boduch');
Readln;
end.
Procedura SetName odczytuje wartość przekazanego do niej parametru i wyświetla go wraz z innym tekstem na
konsoli.
Kilka parametrów procedur lub funkcji
Możliwe jest przekazanie do procedury lub funkcji kilku parametrów tego samego i innego typu. W takim
przypadku wszystkie parametry należy oddzielić znakiem średnika:
function SetName(FName : String; SName : String; Age : Byte);
Aby program został prawidłowo skompilowany, należy przekazać funkcji aż trzy parametry. Jeżeli jednak
występuje kilka parametrów tego samego typu, można oddzielić je przecinkami:
function SetName(FName, SName : String; Age : Byte);
Dzięki temu zapis funkcji jest nieco krótszy. Naturalnie z punktu widzenia kompilatora nie ma znaczenia, jak
zapisana jest deklaracja możliwe jest więc zapisanie tego w taki, czytelny dla nas sposób:
function SetName(FName,
SName : String;
Age : Byte
) : String;
begin
end;
Parametry domyślne
Pamiętasz, jak wspominałem o funkcjach Inc oraz Dec Pierwszy ich parametr musiał być nazwą zmiennej, a
drugi był opcjonalny. Delphi oferuje przydatną możliwość deklarowania parametrów domyślnych. Oznacza to, ze
podczas wywoływania procedury lub funkcji parametr może, ale nie musi zostać wpisany w takim wypadku
Delphi zastosuje domyślną wartość, ustaloną przez programistę. Oto przykład deklaracji takiej procedury:
procedure SetName(FName : String;
SName : String = 'Nieznany';
Age : Byte = 0
);
begin
end;
Parametry domyślne wpisujemy po znaku równości. W przypadku powyższej procedury możliwe jest albo
wywołanie jej w ten sposób:
SetName('Moje imiÄ™');
albo wpisanie wszystkich parametrów:
SetName('Moje imiÄ™', 'Moje nazwisko', 100);
Oba sposoby są prawidłowe jeżeli nie wpiszesz dwóch ostatnich parametrów, Delphi za domyślne wartości
17 z 32 2009-03-14 15:25
Delphi :: Kompendium :: Rozdział 2 - 4programmers.net http://4programmers.net/Delphi/Kompendium/Rozdzia%C5%82_2
uzna te, które zostały wpisane w deklaracji procedury; a zatem dla parametru SName będzie to wartość
Nieznany, a dla Age 0.
Parametry tego samego typu a wartości domyślne
Niestety nie jest możliwe deklarowanie kilku parametrów tego samego typu i jednoczesne przypisanie wartości
jednemu z nich:
procedure SetName(FName,
SName = 'Nieznany' : String;
Age : Byte = 0
);
Powyższa instrukcja spowoduje błąd zawsze podczas określenia wartości domyślnych należy wpisać typ
parametru.
Kolejność wartości domyślnych
Należy jeszcze poczynić jedno istotne zastrzeżenie dotyczące deklaracji wartości domyślnych. Otóż błędna jest
deklaracja wyglądająca w ten sposób:
function Funkcja(X : Integer = 0; Y : Integer) : Integer;
begin
end;
Próbujemy w ten sposób przypisać wartość domyślną pierwszemu parametrowi, a drugiemu nie. Delphi
zaprotestuje i wyświetli błąd: [Error] OverProc.dpr(18): Default value required for 'Y'. Jedynym wyjściem jest
umieszczenie domyślnych parametrów na samym końcu lub deklaracja wartości domyślnych dla każdego
parametru. Taki kod będzie już jak najbardziej prawidłowy:
function Funkcja(Y : Integer; X : Integer = 0) : Integer;
begin
end;
Przeciążanie funkcji i procedur
Stosunkowo nową technologią w Delphi jest możliwość przeciążania procedur i funkcji. Przeciążanie polega na
opatrzeniu funkcji i procedury specjalną klauzulą. Dzięki temu kompilator nie będzie protestował, gdy
zadeklarujemy kilka funkcji lub procedur o tej samej nazwie! Warunkiem jest jednak to, że parametry muszą
być różne, ale nazwa może pozostać ta sama.
Napiszmy przykładowy program, który będzie wykorzystywał funkcje mnożenia przykładowa taka funkcja
może wyglądać tak:
function Mnozenie(X, Y : Integer) : Integer;
begin
Result := X * Y;
end;
Zasada działania jest prosta. Funkcja mnoży dwa parametry X i Y, a następnie zwraca rezultat tego działania.
Jednak podane w funkcji parametry mogą być tylko typu Integer, czyli mogą być wyłącznie liczbami
całkowitymi. W przypadku, gdy chcemy do parametrów przekazać wartości zmiennoprzecinkowe, kompilator
zasygnalizuje błąd. Można oczywiście zamiast typu Integer zastosować chociażby typ Currency. Można także
wykorzystać dwie funkcje Mnozenie o różnych parametrach:
function Mnozenie(X, Y : Integer) : Integer; overload;
begin
Result := X * Y;
end;
function Mnozenie(X, Y : Currency) : Currency; overload;
begin
Result := X * Y;
end;
Żeby wszystko zostało skompilowane dobrze, obie funkcje należy oznaczyć klauzulą overload. Od tej chwili
podczas wywoływania funkcji Mnozenie kompilator sam na podstawie parametrów ustali, jaką funkcję
chcemy wywołać:
Mnozenie(2, 2);
Mnozenie(2.5, 2.5);
Typy parametrów przekazywanych do procedur i funkcji
To, co powiedziałem wcześniej na temat prostego przekazywania parametrów do funkcji i procedur, to nie
wszystko. Istnieje możliwość przekazywania parametrów przez referencję lub przez stałą.
Przekazywanie parametrów poprzez stałą
Umieszczenie w deklaracji parametrów słowa kluczowego const spowoduje przekazywanie parametrów jako
stałych.
procedure Show(const Message : String);
begin
Writeln(Message);
end;
Co to oznacza Procedura nie może w żaden sposób wpływać na zawartość parametru. Próba nadania przez
procedurę jakiejś wartości spowoduje błąd: [Error] ConstParam.dpr(7): Left side cannot be assigned to.
18 z 32 2009-03-14 15:25
Delphi :: Kompendium :: Rozdział 2 - 4programmers.net http://4programmers.net/Delphi/Kompendium/Rozdzia%C5%82_2
Przekazując parametry przez stałą pozwalasz kompilatorowi na maksymalną optymalizację kodu.
Przekazywanie parametrów przez referencję
Przekazywanie parametrów przez referencję polega na umieszczeniu przed parametrami słowa kluczowego var.
Dzięki temu kod znajdujący się wewnątrz procedury może zmienić wartość parametru. Aby lepiej to zrozumieć,
przeprowadzmy małe ćwiczenie. Spójrz na poniższy listing 2.8.
Listing 2.8. Wartości przekazywane przez referencję
program VarParam;
{$APPTYPE CONSOLE}
procedure SetValue(Message : String);
begin
{ próba nadania nowej wartości dla parametru }
Message := 'Hello there!';
end;
var
S : String;
begin
S := 'Hello World';
SetValue(S); // przekazanie zmiennej
Writeln(S); // odczyt wartości zmiennej
Readln;
end.
Teraz zagadka: jaką wartość będzie miała zmienna S Uruchom program i sprawdz! Na ekranie konsoli
zostanie wyświetlony napis Hello World, co oznacza, że procedurze nie udało się zmienić wartości parametru.
Przedefiniuj teraz nieco deklaracjÄ™ procedury SetValue:
procedure SetValue(var Message : String);
begin
{ próba nadania nowej wartości dla parametru }
Message := 'Hello there!';
end;
Dzięki dodaniu słowa var i ponownemu uruchomieniu programu możesz przekonać się, że zmienna S będzie
miała wartość Hello there. Oznacza to, że naszej procedurze udało się zmienić wartość tego parametru.
Dyrektywa out
Zamiast słowa kluczowego var w deklaracji funkcji lub procedury możesz umieścić także słowo out. Zasada
działania dyrektywy out jest bardzo podobna do zasady działania dyrektywy var. Różnica polega na tym, że do
parametru zadeklarowanego jako out nie można przekazać żadnej wartości. Zrozumiesz to na przykładzie
poprzedniego programu zmodyfikuj go do takiej postaci:
program OutParam;
{$APPTYPE CONSOLE}
procedure SetValue(out Message : String);
begin
{ Message nie zawiera wartości! }
Message := 'Hello there!';
end;
var
S : String;
begin
S := 'Hello World';
SetValue(S); // przekazanie zmiennej
Writeln(S); // odczyt wartości zmiennej
Readln;
end.
W kodzie programu zapisana jest próba przekazania wartości do procedury SetValue. Jednak za sprawą
dyrektywy out wartość nie dociera do procedury. Inaczej mówiąc, parametr Message jest pusty. Sprawdz
działanie programu i porównaj, jak program zachowuje się w przypadku, gdy w kodzie zamiast słowa out
znajduje siÄ™ instrukcja var.
Procedury zagnieżdżone
Nic nie stoi na przeszkodzie, aby daną procedurę lub funkcję umieścić w innej procedurze lub funkcji. Wygląda
to mniej więcej tak:
procedure A;
procedure B;
begin
end;
begin
end;
Z powyższego zapisu wynika, że procedura lub funkcja zagnieżdżona (w tym wypadku procedura B) musi
zostać umieszczona przed blokiem begin.
WÅ‚asne typy danych
Object Pascal umożliwia deklarowanie własnych typów danych, które następnie można wykorzystać w
programie. Własny typ danych można zadeklarować za pośrednictwem słowa kluczowego type. Przykładowa
19 z 32 2009-03-14 15:25
Delphi :: Kompendium :: Rozdział 2 - 4programmers.net http://4programmers.net/Delphi/Kompendium/Rozdzia%C5%82_2
deklaracja własnego typu mogłaby wyglądać w ten sposób:
type
TMediumValue = 0..20;
W światku programistów Delphi przyjęło się już, że każdy nowy typ danych poprzedzony jest dużą literą T ja
nie zamierzam od tej reguły odstępować.
Od tej pory możemy w swoim programie używać własnego typu danych TMediumValue, który to typ może
przybrać wartości liczbowe od 0 do 20.
Wykorzystanie takiego typu danych (stworzenie zmiennej) jest już proste:
var
MediumValue : TMediumValue;
Można już korzystać z naszej zmiennej, tak jak z każdej innej:
begin
MediumValue := 10;
end.
Możliwe jest także zadeklarowanie swojego własnego typu wyglądającego tak:
TSamochody = (tsFiat, tsMercedes, tsOpel);
Po utworzeniu zmiennej wskazującej na ten typ będzie ona mogła zawierać jedną z podanych w nawiasie
wartości.
Tablice jako nowy typ
Możliwe jest przekazywanie całych tablic jako parametrów do funkcji lub procedury. Jednak nie da się tego
uczynić bezpośrednio należy w tym celu utworzyć nowy typ danych, np. taki:
type
TMyArray = array[0..20] of String;
Następnie można dopiero przekazać go jako parametr, tak jak to przedstawiono w poniższym przykładzie:
program TypeArray;
{$APPTYPE CONSOLE}
type
TMyArray = array[0..20] of String;
procedure GetArray(MyArray : TMyArray);
begin
{ odebranie tablicy }
end;
var
MyArray : TMyArray;
begin
MyArray[0] := 'Element 1';
{ ... }
end.
Taki program powinien zostać skompilowany i zadziałać bez żadnych problemów.
Aliasy typów
Aliasy służą do tworzenia nowego typu, który w rzeczywistości wskazuje na inny i jest mu równoważny:
Type
TMojTyp = Integer;
Od tego momentu TMojTyp będzie równoważny typowi Integer. Z aliasów możesz korzystać, jeśłi chcesz
zwiększyć czytelność kodu swojego programu. Ogólnie rzecz biorąc, nie jest to często używana funkcja.
Rekordy
Rekordy to zorganizowana struktura danych, połączona w jedną całość. Jest to jakby paczuszka zawierająca
określone elementy. Rekordy również możesz przekazywać jako paczuszkę do funkcji czy procedur w formie
parametru.
Nowe rekordy deklarować można jako nowy typ danych lub jako zmienną, z użyciem słowa kluczowego record.
type
TMyRecord = record
X : Integer;
Y : Integer;
end;
Budowa, jak widzisz, jest specyficzna najpierw należy wpisać nazwę rekordu, a dalej po znaku równości
słowo kluczowe record (uwaga, brak średnika na końcu!). Następnie wypisujemy elementy, z których nasz
rekord ma się składać.
Jako że zadeklarowałem rekord jako nowy typ danych, należy utworzyć dodatkowo zmienną wskazującą na ten
typ. Przy tej okazji poznasz nowy operator Object Pascala kropkę (.). Do poszczególnych pól rekordu
20 z 32 2009-03-14 15:25
Delphi :: Kompendium :: Rozdział 2 - 4programmers.net http://4programmers.net/Delphi/Kompendium/Rozdzia%C5%82_2
odwołujemy się w ten sposób:
MyRecord.X := 1;
Po uprzednim utworzeniu zmiennej wskazujÄ…cej na rekord.
Przekazywanie rekordów jako parametr procedury
Napiszmy prosty program, którzy pobierze dwie liczby wpisane przez użytkownika i przekaże do procedury cały
rekord.
Samo przekazanie rekordu do funkcji przebiega w sposób dość prosty:
type
TMyRecord = record
X : Integer;
Y : Integer;
end;
function Dzielenie(MyRecord : TMyRecord) : Integer;
begin
Result := MyRecord.X div MyRecord.Y;
end;
Delphi wymaga, aby do funkcji Dzielenie przekazany został rekord TMyRecord. Kolejno następuje podzielenie
elementu X przez element Y rekordu i zwrócenie wartości dzielenia. Cały program wygląda tak, jak na listingu
2.9.
Listing 2.9. Rekord przekazany jako parametr procedury
program Recordapp;
{$APPTYPE CONSOLE}
uses SysUtils;
type
TMyRecord = record
X : Integer;
Y : Integer;
end;
function Dzielenie(MyRecord : TMyRecord) : Integer;
begin
Result := MyRecord.X div MyRecord.Y;
end;
var
MyRecord : TMyRecord;
Result : Integer;
begin
Writeln('Podaj pierwszÄ… liczbÄ™');
Readln(MyRecord.X);
Writeln('Podaje drugÄ… liczbÄ™');
Readln(MyRecord.Y);
Result := Dzielenie(MyRecord);
Writeln('Rezultat dzielenia ' + IntToStr(Result));
Readln;
end.
Pobranie danych realizuje polecenie Readln, ale o tym mówiliśmy już na początku tego rozdziału.
W powyższym listingu skorzystałem z funkcji konwersji IntToStr. O konwersji będzie mowa w podpunkcie
Konwersja typów .
Deklaracja rekordu jako zmienna
Nie jest konieczne tworzenie nowego typu dla rekordu. Oznacza to, że zamiast deklarować kolejny rekord jako
nowy typ (type), można zadeklarować go jako zmienną:
var
Rec : record
X, Y : Integer;
end;
Wówczas nie jest konieczne tworzenie nowej zmiennej od razu można zabrać się za przypisywanie danych do
elementów rekordu.
Instrukcja packed
W celu zapewnienia szybszego działania rozmiary rekordów są zaokrąglane do wartości 8 bajtów. Oznacza to, że
po zsumowaniu wszystkich elemenów rekordu i określeniu, ile miejsca zajmie on w pamięci całość
zaokrąglana jest dodatkowo do 8 bajtów.
Umieszczenie instrukcji packed podczas deklaracji rekordu powoduje, że zostanie on skompresowany .
Minusem takiej kompresji jest wolniejsze działanie.
type
TMyRec = packed record
X, Y : Integer;
end;
Instrukcja wiążąca with
Instrukcja with jest przeważnie używana wraz z rekordami lub obiektami (o obiektach będzie mowa w
następnym rozdziale). Nie pełni ona żadnej znaczącej roli uwalnia za to programistę od pisania zbędnego
21 z 32 2009-03-14 15:25
Delphi :: Kompendium :: Rozdział 2 - 4programmers.net http://4programmers.net/Delphi/Kompendium/Rozdzia%C5%82_2
kodu. Załóżmy, że program zawiera następujący rekord:
var
Rec : packed record
X, Y : Integer;
Imie : String[20];
Nazwisko : String[20];
Wiek : Byte;
end;
Prawidłowe wypełnienie rekordu przedstawione jest poniżej:
Rec.X := 12;
Rec.Y := 24;
Rec.Imie := 'Jan';
Rec.Nazwisko := 'Kowalski';
Rec.Wiek := 20;
Dzięki zastosowaniu instrukcji wiążącej with kod ten można skrócić do takiej postaci:
with Rec do
begin
X := 12;
Y := 24;
Imie := 'Jan';
Nazwisko := 'Kowalski';
Wiek := 20;
end;
Możliwe jest określenie długości zmiennej String, co przedstawiłem w powyższym przykładzie. Wystarczy w
deklaracji wpisać w nawiasach kwadratowych maksymalną długość, jaką może posiadać zmienna.
Moduły
Moduł (ang. unit) jest plikiem tekstowym, zawierającym polecenia interpretowane przez kompilator.
Podział programu na moduły pojawił się po raz pierwszy z jednej z wcześniejszych wersji Turbo Pascala.
Zastosowanie modułów pozwala na podział kodu na osobne pliki. Przypomnij sobie przez chwilę poprzedni
rozdział. Podczas tworzenia pierwszego programu i zapisywania pierwszego projektu na dysku został zapisany
plik *.pas. Jest to właśnie plik modułu. Każdemu formularzowi odpowiada jeden moduł, ale z kolei moduł nie
musi być formularzem.
Wyobraz sobie więc, że w swoim programie masz kilka formularzy i w związku z tym wiele plików;
ponadto często korzystasz z jednej procedury. W takim wypadku w każdym z tych formularzy musiałbyś
umieszczać ową procedurę. Zastosowanie w programie modułów zmienia tę sytuację, o czym za chwilę się
przekonasz.
Tworzenie nowego modułu
1. Utwórz nowy projekt.
2. Zamknij Projektanta formularzy oraz Edytor kodu i doprowadz główny plik DPR do takiej postaci:
program UnitApp;
begin
end.
3. Z menu File wybierz polecenie New/Unit. Spowoduje to stworzenie w Edytorze kodu nowej zakładki (nowego
modułu).
4. Z menu File wybierz polecenie Save As plik zapisz pod nazwÄ… MainUnit.
Budowa modułu
Zaraz po stworzeniu nowego modułu Delphi generuje potrzebne instrukcje, które są niezbędnym elementem
tego modułu.
unit MainUnit;
interface
implementation
end.
Nazwa
Pierwszy wiersz zawiera instrukcję unit, która określa nazwę modułu. Nie należy jej zmieniać Delphi generuje
tę nazwę automatycznie, w momencie zapisywania pliku na dysku. Podczas próby zmiany nazwy modułu
wystąpi błąd: [Error] MainUnit.pas(1): Unit identifier 'MainUnitx' does not match file name.
Sekcja Interface
Jest to tzw. część publiczna modułu. Tutaj należy umieszczać deklaracje procedur i funkcji, które mają być
widoczne na zewnątrz , dla innych modułów. W tym momencie należy rozróżnić pewne pojęcia, takie jak
deklaracja oraz definicja.
Deklaracja jest udostępnieniem jedynie nagłówka funkcji lub procedury. Dzięki temu procedury i funkcje są
widoczne dla innych modułów. Np. deklaracja procedury ProcMain może wyglądać tak:
procedure ProcMain(Param1, Param2 : Integer);
Definicja to cały kod procedury lub funkcji, zawarty w sekcji Implementation.
22 z 32 2009-03-14 15:25
Delphi :: Kompendium :: Rozdział 2 - 4programmers.net http://4programmers.net/Delphi/Kompendium/Rozdzia%C5%82_2
Sekcja Implementation
Sekcja Implementation stanowi część prywatną modułu. Kod w niej zawarty w (procedury, funkcje, zmienne,
tablice czy stałe) nie jest widoczny dla innych modułów z punktu widzenia kompilatora dane zawarte w tej
sekcji nie istniejÄ….
W sekcji Implementation należy także umieszczać definicję procedur i funkcji. Przykład prawidłowego modułu
(deklaracji i definicji):
unit MainUnit;
interface
procedure ProcMain(Param1, Param2 : Integer);
implementation
procedure ProcMain(Param1, Param2 : Integer);
begin
end;
end.
Pamiętaj o tym, że również moduł musi być zakończony instrukcją end. (kropka na końcu!).
Włączanie modułu
Podczas poprzedniego ćwiczenia, którego celem było utworzenie nowego modułu, Delphi zwolnił nas z
obowiązku włączenia tego modułu do głównego pliku *.dpr.
Włączenie modułu do programu (lub do innych modułów) realizowane jest za pomocą dyrektywy uses.
uses
MainUnit in 'MainUnit.pas';
Od tej pory wszystkie deklaracje z modułu MainUnit będą widoczne także w obrębie programu głównego. Taka,
wygenerowana przez Delphi, konstrukcja nie jest jedyną poprawną konstrukcją. Wystarczy, jeśli napiszesz
tylko:
uses
MainUnit;
Jeżeli będziesz chciał włączyć do programu kilka modułów, wypisz je jeden po drugim, oddzielając ich nazwy
przecinkami.
Sekcja Initialization oraz Finalization
Dwie wspomniane sekcje Initialization oraz Finalization są opcjonalnymi sekcjami modułu.
Umieszczenie kodu bezpośrednio za sekcją Initialization powoduje wykonanie go zaraz po uruchomieniu
programu. Natomiast kod znajdujący się za sekcją Finalization wykonany zostanie po zakończeniu pracy z
modułem. Przykład przedstawiono w listingu 2.10.
Listing 2.10. Wykorzystanie sekcji Initialization oraz Finalization
unit MainUnit;
interface
uses Dialogs; // włączamy moduł Dialogs
procedure ProcMain(Param1, Param2 : Integer);
implementation
procedure ProcMain(Param1, Param2 : Integer);
begin
end;
initialization
ShowMessage('Rozpoczynamy pracę z modułem...');
finalization
ShowMessage('Kończymy pracę z modułem...');
end.
Po uruchomieniu programu zostanie wyświetlone okienko dialogowe. Tak samo stanie się po zamknięciu naszej
aplikacji.
Najczęściej sekcje Initialization oraz Finalization używane są do przydzielania lub zwalniania danych (pamięci),
przydzielanych w ramach konkretnego modułu.
Jeżeli w programie korzystasz z sekcji Finalization, to koniecznym staje się umieszczenie także sekcji
Initialization. W przeciwnym wypadku próba uruchomienia programu zakończy się błędem: [Error]
MainUnit.pas(17): Declaration expected but 'FINALIZATION' found. Sekcja Initialization może nie zawierać
żadnego kodu istotna jest tylko jej obecność w kodzie.
Konwersja typów
Środowisko Delphi zostało tak zaprojektowane, aby nie dopuszczać do sytuacji, w której zmienna typu Integer
jest przekazywana np. do procedury ShowMessage, która jako parametru wymaga danych typu String. Jednak
tu z pomocą przychodzą nam funkcje konwersji, które pozwalają przekonwertować dane na inny typ. W
rezultacie aby przekazać do procedury ShowMessage zmienną typu Integer, wystarczy umieścić w kodzie taki
23 z 32 2009-03-14 15:25
Delphi :: Kompendium :: Rozdział 2 - 4programmers.net http://4programmers.net/Delphi/Kompendium/Rozdzia%C5%82_2
zapis:
ShowMessage(IntToStr(Zmienna_Integer));
Polecenie IntToStr powoduje konwersjÄ™ danych w postaci Integer na format String.
Funkcje konwersji, przedstawione w tabeli 2.3, zadeklarowane sÄ… w module SysUtils koniecznym staje siÄ™
więc włączenie nazwy tego modułu do listy uses.
Nazwa Opis
IntToStr Konwertuje typ Integer na String
StrToInt Konwertuje typ String na Integer
CurrToStr Konwertuje typ Currency na String
StrToCurr Konwertuje typ String na Currency
DateTimeToStr Konwertuje typ TDateTime na String
StrToDateTime Konwertuje typ String na TDateTime
DateToStr Konwertuje typ TDate na String
StrToDate Konwertuje typ String na TDate
TimeToStr Konwertuje typ TTime na String
TimeStrToTimeToStr Konwertuje typ String na TTime
FloatToStr Konwertuje typ Extended na String
StroToFloat Konwertuje typ String na Extended.
IntToHex Konwertuje typ Integer do postaci heksydemalnej.
StrPas Konwertuje typ String na PChar.
String Konwertuje typ PChar na String.
PChar Konwertuje typ String na PChar.
StrToBool Konwertuje typ String na Boolean.
StrToInt64 Konwertuje typ String na Int64.
Przyznasz, że tych funkcji jest sporo. Nie musisz ich wszystkich pamiętać zawsze możesz sięgnąć do tej
książki. Jednak większość z tych nazw jest intuicyjna i stanowi tylko skrót do konwertowanych typów.
Rzutowanie
Przy rzutowaniu należy zachować szczególną ostrożność. Jest to bowiem sposób na oszukanie kompilatora.
Jeżeli nie jesteś pewien, co robisz, możesz w konsekwencji doprowadzić do wystąpienia poważnych błędów
podczas działania programu.
Najlepiej omówić to na przykładzie. Oto prosty kod zródłowy, który na pewno nie zostanie prawidłowo
skompilowany:
var
VarC : Char;
VarB : Byte;
begin
VarC := 'A';
VarB := VarC;
end.
Dane w postaci Char (pojedynczy znak) próbujemy tu przypisać do zmiennej VarB, która jest zmienną typu
Byte. Oczywiście kompilator wskaże błąd: [Error] typcast.dpr(12): Incompatible types: 'Byte' and 'Char'. Po
drobnej modyfikacji cały program zostanie prawidłowo skompilowany i zadziała bez problemu:
var
VarC : Char;
VarB : Byte;
begin
VarC := 'A';
VarB := Byte(VarC); // < rzutowanie
end.
Rzutowaniem jest właśnie przypisanie danych w ten sposób: Byte(VarC). W takim wypadku rzutujemy typ Char
na Byte, w wyniku czego zmienna VarB będzie posiadać wartość 65 (kod ASCII litery A).
Kolejny przykład tym razem nieco praktyczniejszy, z którego pewnie nieraz skorzystasz. W module Windows
zadeklarowana jest bardzo przydatna funkcja MessageBox. Podobnie jak polecenie ShowMessage, wyświetla ona
okienka informacyjne. Różnica polega na tym, że w przypadku tej funkcji mamy większą kontrolę nad
wyglądem okienka. Nie to jest jednak najważniejsze. Jako parametry tej funkcji należy podać dwie zmienne
typu PChar. Typ PChar jest również typem zmiennych, który umożliwia przechowywanie tekstu, lecz z punktu
widzenia kompilatora są to osobne rodzaje danych. Jednak w tym wypadku rzutowanie typów nie przyniesie
żadnych niechcianych efektów:
program PCharToStr;
uses Windows;
var
lpMessage, lpCaption : String;
begin
24 z 32 2009-03-14 15:25
Delphi :: Kompendium :: Rozdział 2 - 4programmers.net http://4programmers.net/Delphi/Kompendium/Rozdzia%C5%82_2
lpMessage := 'Okienko informacyjne';
lpCaption := 'Zamknięcie programu';
MessageBox(0, PChar(lpMessage), PChar(lpCaption), MB_OK);
end
.
Pierwszy parametr funkcji MessageBox to tzw. uchwyt okna macierzystego. Na razie siÄ™ nim nie przejmuj
możesz w tym miejscu wpisać cyfrę 0. Kolejny parametr to tekst znajdujący się w okienku informacyjnym. Jak
widzisz, konieczne jest rzutowanie na typ PChar. Trzecim parametrem jest tytuł okienka (napis na pasku
tytułowym), a ostatni parametr określa przyciski znajdujące się w oknie.
Więcej na temat funkcji MessageBox możesz dowiedzieć się z pliku pomocy Delphi. Ja chciałem wspomnieć
tylko o tym, że ostatnie parametry mogą być ze sobą łączone za pomocą operatora +. Na przykład takie
wywołanie:
MessageBox(0, PChar(lpMessage), PChar(lpCaption), MB_YESNOCANCEL + MB_ICONWARNING );
spowoduje pojawienie się przycisków Yes, No, Cancel oraz ikonki ostrzeżenia (ang. warning).
Pętle
W światku programistów pod słowem pętla kryje się pojęcie oznaczające ciągłe wykonywanie tych samych
czynności. Jest to bardzo ważny element każdego języka programowania, dlatego konieczne jest zrozumienie
istoty działania tego elementu.
Wyobraz sobie sytuację, w której musisz kilka razy wykonać tę samą czynność, na przykład wyświetlenie kilku
linii tekstu. Zamiast kilkakrotnie pisać instrukcję Writeln, można skorzystać z pętli.
Pętla for..do
Jest to chyba najprostsza z możliwych pętli. Używaj jej zawsze wtedy, gdy wiesz dokładnie, ile wykonań
(iteracji) danej czynności chcesz zastosować. Podczas korzystania z pętli for musisz zadeklarować zmienną,
która za każdym wykonaniem danej czynności będzie przybierać wartość aktualnej iteracji. Żeby to lepiej
zrozumieć, spójrz na przykładową budowę pętli:
for Zmienna := 1 to 10 do { instrukcje }
Pierwszą instrukcją musi być słowo for. Po nim następuje nazwa zmiennej, która musi przybrać wartość
początkową. Następnie kolejne słowo kluczowe to, po którym następuje wartość końcowa pętli. Powyższa
konstrukcja spowoduje 10-krotne wykonanie poleceń umieszczonych po słowie do. Podsumowując, budowa
pętli przedstawia się następująco:
for Zmienna := {Wartość początkowa} to {Wartość końcowa} do {instrukcje }
Przykładowy program wyświetla na ekranie konsoli następujący tekst:
Odliczanie 1
Odliczanie 2
...
W celu zrealizowania tego zadania bez korzystania z pętli należałoby 10 razy przepisać instrukcję Writeln, co
jest po prostu stratÄ… czasu.
program ForLoop;
{$APPTYPE CONSOLE}
uses
SysUtils;
var
I : Integer; // deklaracja zmiennej
begin
for I := 1 to 10 do
Writeln('Odlicznie... ' + IntToStr(i));
Readln;
end.
Uruchom program i sprawdz jego działanie. Pozmieniaj wartość początkową i końcową, aby sprawdzić, jakie
będzie zachowanie programu.
Odliczanie od góry do dołu
Pętla, jaką przedstawiłem wyżej, realizuje odliczanie od dołu (wartości mniejszej) do góry (wartość wyższa).
Istnieje możliwość odliczania odwrotnego, czyli od wartości wyższej do mniejszej. W tym celu słowo kluczowe
to należy zastąpić słowem downto. Wystarczy zatem drobna zmiana pętli:
for I := 10 downto 1 do
Writeln('Odlicznie... ' + IntToStr(i));
Teraz program wykona pętlę z wartością początkową równą 10.
Z pętlą for wiąże się jedno ostrzeżenie kompilatora. Otóż w przypadku, gdy pętla for znajduje się wewnątrz
procedury lub funkcji, zmienna pomocnicza musi być zmienną lokalną. Przykładowo próba skompilowania
takiego kodu:
var
I: Integer; // deklaracja zmiennej
procedure Loop;
25 z 32 2009-03-14 15:25
Delphi :: Kompendium :: Rozdział 2 - 4programmers.net http://4programmers.net/Delphi/Kompendium/Rozdzia%C5%82_2
begin
for I := 0 to 100 do { instrukcje }
end;
wiąże się z wystąpieniem ostrzeżenia: [Warning] LoopBreak.dpr(10): For loop control variable must be simple
local variable. Kompilator próbuje Cię ostrzec, iż zmienna (w tym przypadku I) może być modyfikowana przez
inne procedury, a to nie jest zalecane. Umieszczenie deklaracji zmiennej wewnÄ…trz procedury Loop pozwoli na
prawidłową kompilację programu.
Pętla while..do
Niekiedy nie jesteś w stanie określić, ile iteracji będzie wymagała pętla; może nie będzie wymagana żadna
iteracja, a może potrzebne będą ich setki W takim przypadku należy skorzystać z pętli while. Budowa takiej
pętli jest następująca:
while {Warunek do spełnienia} do
{ instrukcje }
Pętla będzie wykonywana, dopóki warunek zapisany pomiędzy słowami kluczowymi while i do nie zostanie
spełniony.
Napiszmy prosty program, polegający na pobieraniu hasła dostępu. Jeżeli hasło będzie błędne, pętla zostanie
wykonana po raz drugi; jeżeli hasło będzie poprawne nastąpi zakończenie programu.
program WhileLoop;
{$APPTYPE CONSOLE}
uses
SysUtils;
var
Password : String; // deklaracja zmiennej
begin
while Password <> 'delphi7' do
begin
Writeln('Podaj hasło...');
Readln(Password);
end;
Writeln('Hasło poprawne!');
Readln;
end.
Omówię pokrótce program. Na samym początku umieszczamy warunek:
while Password <> 'delphi7' do
Za jego pomocą sprawdzamy, czy zmienna Password jest różna od delphi7 jeżeli tak jest, następuje
wykonanie instrukcji pętli znajdującej się w bloku begin..end. Jeżeli wpisane hasło jest niepoprawne,
wykonywana zostaje kolejna iteracja; w przeciwnym wypadku pętla kończy swoje działanie.
Pętla repeat..until
Efekt zastosowania pętli repeat jest bardzo podobny do działania pętli while pętla ta także może być
wykorzystywana w nieskończoność . Jedyna różnica polega na tym, że w pętli repeat warunek zakończenia
sprawdzany jest dopiero na końcu wykonania. Oznacza, to, że pętla repeat zawsze będzie wykonana co
najmniej raz. Dopiero po tej iteracji program sprawdzi, czy można wyjść z pętli. W przypadku pętli while
warunek sprawdzany jest bezpośrednio przed jej wykonaniem, co w rezultacie może spowodować, że taka pętla
nigdy nie zostanie wykonana.
Budowa pętli repeat jest następująca:
repeat
{ instrukcje do wykonania }
until { warunek zakończenia }
Pętla repeat, użyta w poprzednim przykładzie, wyglądałaby następująco:
repeat
Writeln('Podaj hasło...');
Readln(Password);
until Password = 'delphi7';
Rezultat działania byłby identyczny.
Z pętlami wiąże się niebezpieczeństwo zapętlenia . Należy uważać, aby program nie wykonywał stale tych
samych czynności może do tego dojść, jeżeli warunek zakończenia nigdy nie zostanie spełniony.
Procedura Continue
Polecenie Continue może być używane tylko wraz z pętlami. Powoduje ono przejście do następnego wywołania
pętli bez wykonywania dalszych instrukcji.
Oczywiście najlepiej istotę działania procedury Continue poznasz na przykładzie. Załóżmy, że mamy pętlę for,
która zostanie wykonana 10 razy. Za każdym razem program losuje jakąś liczbę z przedziału od 1 do 3 i na
podstawie tej wylosowanej liczby wyświetla jakiś tekst. Dzięki poleceniu Continue możemy sprawić, aby w
przypadku, gdy wylosowaną liczbą będzie 1, ominąć wyświetlenie tekstu i przejść do następnej iteracji (listing
2.11.).
Listing 2.11. Zastosowanie instrukcji Continue w pętli for
program LoopContinue;
26 z 32 2009-03-14 15:25
Delphi :: Kompendium :: Rozdział 2 - 4programmers.net http://4programmers.net/Delphi/Kompendium/Rozdzia%C5%82_2
{$APPTYPE CONSOLE}
var
I, Number : Integer; // deklaracja zmiennej
begin
Randomize;
for I := 1 to 10 do
begin
Number := Random(3)+1;
if Number = 1 then Continue;
case Number of
1: Writeln('Uuu, wylosowałeś 1');
2: Writeln('No, dwa... jeszcze może być');
3: Writeln('Dobrze');
end;
end;
Readln;
end.
InteresujÄ…cy nas warunek znajduje siÄ™ w tym miejscu: if Number = 1 then Continue;. Kompilator odczytuje
to tak: jeżeli zmienna Number zawiera wartość 1, pomiń wykonywanie dalszych instrukcji i przejdz od razu do
kolejnej iteracji.
Procedura Break
Polecenie Break również może być wykonane tylko w połączeniu z pętlami. W odróżnieniu od procedury
Continue umożliwia ono wyjście z pętli (opuszczenie jej). Po napotkaniu instrukcji Break dalsze wykonywanie
pętli zostaje wstrzymane, a program wykonuje polecenia umieszczone po pętli.
Zmodyfikujmy ostatni przykład, zastępując procedurę Continue poleceniem Break:
program LoopBreak;
{$APPTYPE CONSOLE}
var
I, Number : Integer; // deklaracja zmiennej
begin
Randomize;
for I := 1 to 10 do
begin
Number := Random(3)+1;
if Number = 1 then
begin
Writeln('Wylosowano 1 opuszczamy pętle...');
Break;
end;
case Number of
1: Writeln('Uuu, wylosowałeś 1');
2: Writeln('No, dwa... jeszcze może być');
3: Writeln('Dobrze');
end;
end;
Readln;
end.
Jeżeli program wylosuje cyfrę 1, program wyświetli stosowną informację i zakończy działanie pętli. Żaden kod
umieszczony poniżej instrukcji Break nie zostanie wykonany.
Zbiory
Zbiory są kolekcją danych tego samego typu. To zdanie zapewne niezbyt wyjaśnia funkcję zbiorów, spójrz więc
na ten kod:
type
TCar = (tcFiat, tcSkoda, tcOpel, tcFerrari, tcPorshe, tcPeugeot);
TCarSet = set of TCar;
W drugim wierszu znajduje siÄ™ deklaracja nowego typu danych TCar. Zmienna korzystajÄ…ca z tego typu
może zawierać jedną z wartości podanych w nawiasie. Natomiast typ TCarSet jest zbiorem danych TCar. Nowy
zbiór deklaruje się za pomocą konstrukcji set of. Jak już mówiłem, zbiory są konstrukcją mogącą zawierać
elementy określonych danych. Znaczy to, że zmienna korzystająca z typu TCarSet może zawierać np. wszystkie
elementy lub tylko kilka spośród nich. Przy wykorzystaniu zwykłych zmiennych nie jest to możliwe, gdyż do
takiej zmiennej można przypisać tylko jeden element TCar.
Możliwa jest także deklaracja bezpośrednia, czyli deklaracja bez tworzenia dodatkowego typu TCar:
type
TCarSet = set of (tcFiat, tcSkoda, tcOpel, tcFerrari, tcPorshe, tcPeugeot);
Zbiory mogą być również zbiorami liczbowymi lub zawierającymi pojedyncze znaki:
program Sets;
{$APPTYPE CONSOLE}
type
27 z 32 2009-03-14 15:25
Delphi :: Kompendium :: Rozdział 2 - 4programmers.net http://4programmers.net/Delphi/Kompendium/Rozdzia%C5%82_2
TCarSet = set of (tcFiat, tcSkoda, tcOpel, tcFerrari, tcPorshe, tcPeugeot);
TNumberSet = set of 0..9;
TCharSet = set of 'A'..'Z';
Przypisywanie elementów zbioru
Chcąc przypisać elementy zbioru do danej zmiennej, trzeba skorzystać z nawiasów kwadratowych.
var
CarSet : TCarSet;
begin
CarSet := [tcSkoda, tcOpel];
Powyższy przykład powoduje przypisanie do zbioru dwóch elementów tcSkoda i tcOpel. Możliwe jest
oczywiście stworzenie kilku zmiennych korzystających z danego zbioru:
var
Tanie,
Srednie,
Drogie : TCarSet;
begin
Tanie := [];
Srednie := [tcFiat, tcSkoda, tcOpel, tcPeugeot];
Drogie := [tcPorshe, tcFerrari];
end.
Do zmiennej Tanie przypisujemy zbiór pusty po prostu nie zawiera on elementów, symbolizują go więc
jedynie dwa nawiasy.
Odczytywanie elementów ze zbioru
Wraz ze zbiorami często używany jest operator in. Służy on do sprawdzania, czy dany element należy do
określonego zbioru. Przykładowo:
if (tcFiat in Cars) then { czynności }
Zwróć uwagę na konstrukcję. Na początku należy wpisać nazwę elementu, a dopiero pózniej zmienną
wskazującą na zbiór. Jeżeli dany element należy do zbioru, wykonany zostanie kod znajdujący się po słowie
then.
Zaprzeczanie
Możesz zapytać: co stanie się, gdy chcemy sprawdzić, czy dany element nie należy do zbioru ??. W takim
wypadku zamiast nie możemy operatora in wpisać out, ale możliwe jest zastosowanie operatora not, który jest
zaprzeczeniem (o operatorach pisałem nieco wyżej).
if not (tcFiat in Cars) then { czynności }
Jeżeli element tcFiat nie należy do zbioru Cars, warunek zostanie spełniony.
Przekazywanie zbioru jako parametru procedury
Często podczas programowania w Object Pascalu natkniesz się na konstrukcję, która wymaga przekazania
zbioru jako parametru procedury. Jeżeli więc podczas kompilacji wyświetlony zostanie błąd: [Error]
Sets.dpr(20): Incompatible types: 'TCarSet' and 'Enumeration', będzie to oznaczało, że podany parametr musi
być zbiorem, czyli musi być przekazany w nawiasach kwadratowych. Oto przykład takiego programu:
program Sets;
{$APPTYPE CONSOLE}
type
TCarSet = set of (tcFiat, tcSkoda, tcOpel, tcFerrari, tcPorsche, tcPeugeot);
procedure CoKupujemy(Cars : TCarSet);
begin
if (tcFiat in Cars) then Writeln('Kupujemy Fiata!');
if (tcSkoda in Cars) then Writeln('Kupujemy Skode!');
if (tcOpel in Cars) then Writeln('Kupujemy Opla!');
if (tcFerrari in Cars) then Writeln('Kupujemy Ferrari!');
if (tcPorsche in Cars) then Writeln('Kupujemy Porsche!');
if (tcPeugeot in Cars) then Writeln('Kupujemy Peugeota!');
end;
begin
CoKupujemy([tcPorsche, tcFerrari]);
Readln;
end.
Dodawanie i odejmowanie elementów zbioru
W celu dodania do zbioru lub odjęcia z niego jakiegoś elementu można skorzystać z operatorów + i . Trzeba
to jednak zapisać w specyficzny sposób:
CarSet := CarSet + [tcFiat];
Za pomocą tego polecenia dodajemy do zbioru element tcFiat. Taka konstrukcja jest wymagana, gdyż
gdybyśmy napisali tak:
28 z 32 2009-03-14 15:25
Delphi :: Kompendium :: Rozdział 2 - 4programmers.net http://4programmers.net/Delphi/Kompendium/Rozdzia%C5%82_2
CarSet := [tcFiat];
spowodowałoby to wymazanie elementów poprzednio znajdujących się w zbiorze i dodanie jedynie tcFiat.
Include i Exclude
Zalecaną metodą dodawania oraz odejmowania elementów zbioru są funkcje Include oraz Exclude. Pierwsza z
nich włącza element do zbioru, a druga odejmuje. Ich użycie jest zalecane ze względu na to, że działają o wiele
szybciej niż operacje z zastosowaniem znaków + i . Przykład użycia:
Include(CarSet, tcFiat); // dodawanie
Exclude(CarSet, tcPorshe); // odejmowanie
Wskazniki
Wskazniki to najtrudniejsza do opanowania czynność programistyczna zarówno w Object Pascalu, jak i w
innych językach programowania.
Zazwyczaj podczas przypisywania danych do zmiennej Delphi rezerwuje obszar w komórkach pamięci i tam
umieszcza dane zmiennej. Gdy chcemy w programie odwołać się do zmiennej, Delphi na podstawie jej nazwy
odszukuje komórkę pamięci, w której umieszczone są dane.
Wskazniki to zmienne, które wskazują na inną zmienną.
Zapewne powyższa wskazówka niewiele Ci wyjaśnia. Wskazniki to specjalny typ danych w pamięci nie są
przechowywane dane jako takich, lecz jedynie odpowiadające im adresy komórki pamięci.
Tworzenie wskaznika
Zadeklarowania wskaznika dokonuje siÄ™ za pomocÄ… operatora (^).
var
P : ^String;
Od tego momentu w programie możemy korzystać ze wskaznika P, wskazującego typ String. We wszelkich
operacjach dokonywanych na wskaznikach muszą być wykorzystywane dwa operatory specyficzne jedynie dla
typów wskaznikowych są to operatory ^ oraz @. Ich znaczenie poznasz w dalszej części tego rozdziału.
Przydział danych do wskazników
Na samym początku przeprowadzmy pewien test. Spróbuj uruchomić taki program:
program Pointers;
var
P : ^String;
begin
P^ := 'Delphi 7';
end.
Program próbuje przypisać określone dane do wskaznika w postaci łańcucha tekstowego; musi to się odbyć z
wykorzystaniem operatora ^, w przeciwnym wypadku Delphi zasygnalizuje błąd: [Error] Pointers.dpr(12):
Incompatible types: 'String' and 'Pointer'.
Próba uruchomienia takiego programu zakończy się jednak błędem typu Runtime (patrz rysunek 2.2).
Rysunek 2.2. Komunikat o błędzie wyświetlony po uruchomieniu programu
Przyczyną błędu jest fakt, że do typu wskaznikowego nie można przypisać w normalny sposób wartości.
Wskazniki muszą uprzednio wskazywać na inną, zwykłą zmienną.
Przydzielaniem danych bezpośrednio do wskaznika zajmiemy się w dalszej części tego
rozdziału.
Poniższy program zostanie skompilowany i, co najważniejsze, zadziała bez problemu:
program Pointers;
var
S : String;
P : ^String;
begin
S := 'Delphi'; // przypisanie danych do zwykłej zmiennej
P := @S; // uzyskanie adresu zmiennej
P^ := 'Borland Delphi 7 Studio'; // modyfikacja danych
end.
Uzyskiwanie adresów zmiennej
29 z 32 2009-03-14 15:25
Delphi :: Kompendium :: Rozdział 2 - 4programmers.net http://4programmers.net/Delphi/Kompendium/Rozdzia%C5%82_2
Jak widać w powyższym przykładzie, po zadeklarowaniu zmiennej S (typu String) wskaznik musi uzyskać jej
adres. Realizuje to operator @. Od tego momentu wskaznik P wskazuje na zmienną S. Poniższa instrukcja:
P^ := 'Borland Delphi 7 Studio';
w rzeczywistości spowoduje zmianę wartości zmiennej S! Dzieje się tak dlatego, że wskaznik P wskazuje na
zmienną S. A zatem zmieniając wartość wskaznika, w rzeczywistości zmieniamy wartość zmiennej! Możesz to
sprawdzić, dodając na końcu programu jeszcze jedną instrukcję:
MessageBox(0, PChar(S), '', 0);
W okienku informacyjnym będzie widniał napis Borland Delphi 7 Studio.
Do czego to służy?
To jest dobre pytanie! Można się zastanowić, do czego służą wskazniki? . Założenie jest takie, że podczas
tworzenia jakichś struktur zarówno tablic, jak i rekordów nie jest konieczne manipulowanie wielkimi
blokami pamięci. Wystarczy tylko stworzyć wskaznik tego rekordu i ewentualnie modyfikować w ten sposób
dane, zamiast tworzyć kolejną instancję (kopię) rekordu.
Tworzenie wskazników na rekordy
Właśnie teraz przedstawię Ci przykład tego, o czym mówiłem wcześniej. Podczas tworzenia jakiegoś rekordu
wskazane jest utworzenie nowego typu wskaznikowego, wskazującego na ten rekord. Po wypełnieniu danych do
procedury jest przekazywany jedynie wskaznik tego rekordu:
program PRecApp;
uses
Dialogs;
type
TInfoRec = packed record
FName : String[30];
SName : String[30];
Age : Byte;
Pesel : Int64;
Nip : String[60]
end;
PInfoRec = ^TInfoRec; // utworzenie wskaznika
procedure SomeProc(InfoRec : PInfoRec);
begin
ShowMessage('Dotychczasowa wartość InfoRec.FName to ' + InfoRec.FName + '. Zmieniam na
Adam');
InfoRec.FName := 'Adam'; // zmiana danych
end;
var
InfoRec: TInfoRec;
begin
InfoRec.FName := 'Jan';
InfoRec.SName := 'Kowalski';
InfoRec.Age := 41;
InfoRec.Pesel := 55012010013;
InfoRec.Nip := '34234 23432 23423';
SomeProc(@InfoRec);
ShowMessage(InfoRec.FName); // wyświetlenie zmienionej wartości
end.
Wskaznik odczytuje dane rekordu InfoRec z pamięci możliwa jest także zamiana tych danych, co
zaprezentowałem w powyższym listingu.
Przydział i zwalnianie pamięci
Na samym początku omawiania wskazników zaprezentowałem przykład, w którym próbowaliśmy przydzielić
dane do wskaznika. Uruchomienie tamtego programu skończyło się błędem, dlatego że nie zaalokowaliśmy
pamięci dla tych wskazników. Pamięć można zaalokować (przydzielić) za pomocą funkcji New.
Stos to cały obszar pamięci rezerwowany dla aplikacji w trakcie jej uruchamiania.
Sterta to cała dostępna pamięć komputera oraz ilość wolnego miejsca na dysku komputera.
Po wywołaniu funkcji New program automatycznie alokuje w sposób dynamiczny pamięć dla rekordu. Po
skończeniu pracy z rekordem pamięć należy zwolnić za pomocą polecenia Dispose (listing 2.12.)
Listing 2.12. Przydział pamięci dla rekordu
program NewPointer;
uses
Dialogs;
type
TInfoRec = packed record
FName : String[30];
SName : String[30];
Age : Byte;
Pesel : Int64;
Nip : String[60]
30 z 32 2009-03-14 15:25
Delphi :: Kompendium :: Rozdział 2 - 4programmers.net http://4programmers.net/Delphi/Kompendium/Rozdzia%C5%82_2
end;
PInfoRec = ^TInfoRec; // utworzenie wskaznika
var
InfoRec: PInfoRec;
begin
New(InfoRec);
InfoRec^.FName := 'Jan';
InfoRec^.SName := 'Kowalski';
InfoRec^.Age := 41;
InfoRec^.Pesel := 55012010013;
InfoRec^.Nip := '34234 23432 23423';
ShowMessage(InfoRec^.FName); // wyświetlenie zmienionej wartości
Dispose(InfoRec);
end.
W celu zaalokowania pamięci można posłużyć się także procedurami GetMem i FreeMem. Funkcja GetMem
wymaga wpisania dodatkowego parametru, jakim jest ilość bajtów przeznaczonych do alokacji. Dane te
uzyskujemy, wywołując funkcję SizeOf np.:
GetMem(InfoRec, SizeOf(InfoRec));
Zalecane jest jednak użycie funkcji New i Dispose.
Wartość pusta
Nieraz podczas programowania spotkasz się z instrukcją nil. Instrukcja ta używana jest wraz ze wskaznikami i
oznacza wartość pustą.
Wskaznik := nil;
Taki zapis spowoduje, że do wskaznika nie będą aktualnie przypisane żadne wartości.
Pliki dołączane
Idea plików dołączanych jest bardzo prosta polega na włączeniu w odpowiednim miejscu modułu pliku
tekstowego, który jest traktowany jak integralna jego część.
Plik dołączany to nic innego, jak zwykły plik tekstowy. Z menu File wybierz pozycje New/Other. W oknie
dialogowym kliknij ikonę Text. W Edytorze kodu pojawi się nowa zakładka zapisz ten plik pod nazwą
SHOW.INC, ale uprzednio wpisz w Edytorze kodu prostÄ… instrukcjÄ™:
ShowMessage('Hello World!');
Plik główny DPR powinien wyglądać tak:
program IncludeInc;
uses Dialogs;
begin
{$I SHOW.INC}
end.
Dzięki dyektywie {$I} można włączyć plik do programu; będzie to równoważne wstawieniu w tym miejscu
zawartości owego pliku SHOW.INC.
Etykiety
Chociaż wielu programistów sprzeciwia się używaniu etykiet, ja omówię tutaj tę technologię, gdyż nieraz
możesz być zmuszony do skorzystania z niej.
Etykiety, mówiąc prosto, to miejsce w kodzie (zakładka), do którego można zawsze przejść (przeskoczyć).
Najpierw jednak taką etykietę należy zadeklarować np. tak, jak zmienne, tyle że za pomocą słowa
kluczowego label.
label
Moja_Etykieta;
Dzięki takiej deklaracji kompilator wie , że ma do czynienia z etykietą. Przejście do takiej etykiety ogranicza
się do wywołania słowa kluczowego goto Nazwa_Etykiety;
program Labels;
{$APPTYPE CONSOLE}
uses SysUtils;
label
Moja_Etykieta;
var
I : Integer;
begin
Randomize;
Moja_Etykieta: I := Random(10);
31 z 32 2009-03-14 15:25
Delphi :: Kompendium :: Rozdział 2 - 4programmers.net http://4programmers.net/Delphi/Kompendium/Rozdzia%C5%82_2
Writeln('Wylosowałem ' + IntToStr(I));
if I = 5 then goto Moja_Etykieta; // jezeli komputer wylosuje 5 - ponów losowanie
Readln;
end.
Samą zakładkę ustawia się, wpisując jej nazwę, a po dwukropku dalszą część kodu. Powyższy program
realizuje losowanie liczby; jeżeli wylosowana zostanie liczba 5, program przechodzi do ustawionej wcześniej
etykiety.
Podsumowanie
Przyznam szczerze, że dla Ciebie mógł to być trudny, a zarazem niezbyt ciekawy rozdział tej książki. Cóż, przed
przystąpieniem do tworzenia poważniejszych aplikacji w Delphi należało poznać podstawową składnię. Teraz
masz już solidne podstawy do dalszej nauki. Nie martw się, jeżeli nie zrozumiałeś wszystkiego; jest to
naturalne. Wiadomo, że nie jesteś w stanie w tak krótkim czasie zapamiętać wszystkiego naraz. Powracaj do
tego rozdziału w razie, gdy czegoś zapomnisz.
Załączniki:
Listingi_2.zip (21.20 kB)
Więcej informacji
Delphi 2005. Kompendium
programisty
Adam Boduch
Format: B5, stron: 1048
oprawa twarda
Zawiera CD-ROM
« RozdziaÅ‚ 1. Podstawy Delphi. Spis treÅ›ci RozdziaÅ‚ 3. Programowanie
obiektowe
© Helion 2003. Autor: Adam Boduch. Zabrania siÄ™ rozpowszechniania tego tekstu bez zgody autora.
Kategoria: Kompendium
Ostatnia modyfikacja 04-05-2008 02:44 Ostatni autor whopper
Ilość wyświetleń 57181 Wersja 12
JaskMar dnia 18-07-2008 15:21
cytat:
"Pętla będzie wykonywana, dopóki warunek zapisany pomiędzy słowami kluczowymi while i do nie
zostanie spełniony."
Zdeka odwrotnie!
kakazx dnia 02-03-2008 16:38
Writeln();i readln();nie działa oddoje I/O Erro 105
hofas dnia 09-08-2007 16:46
"Byte zżera jedynie 2 bajty" ; oczywiscie 1 bajt a nie dwa chyba ze o czyms nie wiem....
Dodaj komentarz
32 z 32 2009-03-14 15:25
Wyszukiwarka
Podobne podstrony:
Delphi Kompendium Roz10Delphi Kompendium Roz14Delphi Kompendium Roz5Delphi Kompendium Roz12Delphi Kompendium Roz8Delphi Kompendium Roz6Delphi 2005 Kompendium programistyKompendium cz 2Delphi i PHPDelphi Leksykon kieszonkowy?lplkkompendiumToczenie Kompensacja zaokrąglenia ostrzakompendiumwięcej podobnych podstron