background image

 

 

 

background image

 

Wydano za zgodą Rektora 

 
 
 

Materiały pomocnicze do zajęć z przedmiotu „Wstęp do programowania”  

dla studentów kierunku informatyka.  

 

 

 

W procesie wydawniczym pominięto  

etap opracowania językowego.  

Wersja elektroniczna materiałów  

została przygotowana przez Autorów. 

 

 

 
 

algorytm, program, instrukcja, 

iteracja, zmienna plikowa, tablica, funkcja, 

 procedura, rekord, wskaźnik, stos, moduł, obiekt, 

 klasa, konstruktor, metoda, aplikacja 

 
 

 
 
 

ISBN 978-83-7199-862-7 

 

Oficyna Wydawnicza Politechniki Rzeszowskiej 

al. Powstańców Warszawy 12, 35-959 Rzeszów 

 

e-mail: oficyna1@prz.rzeszow.pl 

 

 
 

 

background image

 

 

SPIS TREŚCI 

 

 

1.

 

Wprowadzenie do programowania ......................................... 7

 

1.1.

 

Kryteria wyboru języka programowania ............................. 7

 

1.2.

 

Efektywność języków niskiego, średniego i wysokiego 
poziomu ................................................................................ 8

 

1.3.

 

Język Pascal w ujęciu historycznym .................................... 9

 

1.4.

 

Ś

rodowiska programistyczne uŜywające języka Pascal .... 10

 

1.5.

 

Etapy tworzenia programu komputerowego ...................... 14

 

1.6.

 

Cechy dobrego programu komputerowego ........................ 14

 

1.7.

 

Kod źródłowy programu w kontekście języka 
programowania .................................................................. 15

 

1.8.

 

Rola kompilatora w procesie powstawania programu ....... 17

 

1.9.

 

Architektura kompilatora ................................................... 18

 

1.10.

 

Podział kompilatorów ze względu na platformę ................ 19

 

1.11.

 

Podział kompilatorów ze względu na liczbę przejść ......... 20

 

 

2.

 

Algorytmy i schematy blokowe ............................................. 22

 

2.1.

 

Podstawowe cechy algorytmów ......................................... 22

 

2.2.

 

Klasyfikacja algorytmów ze względu na sposób 
implementacji ..................................................................... 23

 

2.3.

 

Klasyfikacja algorytmów ze względu na sposób 
zaprojektowania ................................................................. 24

 

2.4.

 

Właściwości schematów blokowych ................................. 26

 

2.5.

 

Schematy blokowe wybranych algorytmów ...................... 27

 

2.6.

 

Zadania ............................................................................... 34

 

 

3.

 

Podstawowe elementy języka Pascal ..................................... 36

 

3.1.

 

Zalety języka Pascal ........................................................... 36

 

3.2.

 

Standardowe symbole i słowa kluczowe ........................... 36

 

background image

 

3.3.

 

Sposoby zapisu liczb .......................................................... 37

 

3.4.

 

Sposoby notowania komentarzy ........................................ 38

 

3.5.

 

Zasady deklarowania stałych i zmiennych ........................ 39

 

3.6.

 

Definicja typu i rodzaje typów danych .............................. 40

 

3.7.

 

Rodzaje operatorów ........................................................... 49

 

3.8.

 

Priorytet operatorów .......................................................... 54

 

3.9.

 

Struktura programu ............................................................ 54

 

 

4.

 

Instrukcje ................................................................................ 57

 

4.1.

 

Instrukcje proste ................................................................. 57

 

4.2.

 

Instrukcja złoŜona .............................................................. 58

 

4.3.

 

Instrukcja warunkowa ........................................................ 59

 

4.4.

 

Instrukcja wyboru .............................................................. 61

 

4.5.

 

Instrukcje iteracyjne ........................................................... 63

 

4.6.

 

Procedury Break i Continue ............................................... 70

 

4.7.

 

Pętle i tablice ...................................................................... 71

 

4.8.

 

Zadania ............................................................................... 74

 

 

5.

 

Procedury i funkcje ................................................................ 75

 

5.1.

 

Składnia procedury ............................................................ 75

 

5.2.

 

Przekazywanie parametrów do procedur i funkcji ............ 77

 

5.3.

 

Procedury i dyrektywa zapowiadająca Forward ............... 80

 

5.4.

 

Składnia funkcji ................................................................. 81

 

5.5.

 

PrzeciąŜanie funkcji ........................................................... 85

 

5.6.

 

Wywołanie funkcji przez inną funkcję .............................. 86

 

5.7.

 

Zmienne globalne i lokalne ................................................ 87

 

5.8.

 

Funkcje rekurencyjne ......................................................... 90

 

5.9.

 

Typ funkcyjny .................................................................... 91

 

5.10.

 

Procedury kierujące działaniem programu ........................ 92

 

5.11.

 

Kryteria stosowania procedur i funkcji .............................. 94

 

background image

 

5.12.

 

Moduły ............................................................................... 95

 

5.13.

 

Zadania ............................................................................... 97

 

 

6.

 

Operacje na plikach ............................................................... 98

 

6.1.

 

Rodzaje plików .................................................................. 98

 

6.2.

 

Etapy przetwarzania pliku .................................................. 99

 

6.3.

 

Przykładowe programy działające na plikach ................. 102

 

6.4.

 

Zadania ............................................................................. 104

 

 

7.

 

Wskaźniki .............................................................................. 106

 

7.1.

 

Definicja zmiennej wskaźnikowej ................................... 106

 

7.2.

 

Procedury dynamicznego rezerwowania i zwalniania 
pamięci ............................................................................. 107

 

7.3.

 

Wskaźniki i tablice ........................................................... 111

 

7.4.

 

Operacje arytmetyczne na wskaźnikach .......................... 113

 

 

8.

 

Struktury dynamiczne.......................................................... 114

 

8.1.

 

Kolejka ............................................................................. 114

 

8.2.

 

Lista jednokierunkowa ..................................................... 114

 

8.3.

 

Stos jako dynamiczna struktura danych ........................... 117

 

8.4.

 

Zadania ............................................................................. 121

 

 

9.

 

Klasy ...................................................................................... 123

 

9.1.

 

Zasady deklarowania klasy .............................................. 123

 

9.2.

 

Istota programowania obiektowego ................................. 124

 

9.3.

 

Konstruktor i destruktor ................................................... 128

 

9.4.

 

Typy konstruktorów ......................................................... 130

 

9.5.

 

Dziedziczenie ................................................................... 133

 

9.6.

 

Polimorfizm ..................................................................... 134

 

9.7.

 

Metody wirtualne ............................................................. 136

 

9.8.

 

Metody dynamiczne ......................................................... 138

 

background image

 

 

10.

 

Programowanie obiektowe - aplikacje ............................... 141

 

10.1.

 

Komponenty LCL ............................................................ 141

 

10.2.

 

Inspektor obiektów ........................................................... 144

 

10.3.

 

Okno komunikatów i okno Form ..................................... 145

 

10.4.

 

Edytor źródeł .................................................................... 145

 

10.5.

 

Obiekt Button1 klasy TButton ......................................... 147

 

10.6.

 

Aplikacja Stoper ............................................................... 150

 

10.7.

 

Aplikacja Kalkulator ........................................................ 154

 

10.8.

 

Aplikacja Równanie kwadratowe .................................... 156

 

10.9.

 

Rysowanie figur geometrycznych.................................... 161

 

10.10.

 

Tablice i komponent TListBox .................................... 166

 

10.11.

 

Okna dialogowe ........................................................... 170

 

10.12.

 

Tablice jednowymiarowe i komponent TStringGrid ... 173

 

10.13.

 

Tablice dwuwymiarowe i komponent TStringGrid ..... 176

 

10.14.

 

Zadania ......................................................................... 180

 

 

LITERATURA ........................................................................... 181

 

 

 

 

background image

 

1.

 

Wprowadzenie do programowania 

 

Programowanie  komputerów  (pisanie  programów  lub  skryptów,  kodowa-

nie) jest formą działalności twórczej człowieka i polega na pisaniu uŜytecznych, 
moŜliwych do przechowywania i rozszerzania, zestawów instrukcji, które mogą 
być  interpretowane  (wykonywane)  przez  system  komputerowy  (komputer). 
Instrukcje,  reprezentujące  m.in.  operacje  arytmetyczne  i  logiczne,  czynią  pracę 
umysłową uŜytkownika komputera prostszą i bardziej efektywną. 

Programowanie  komputerów  to  czynność  polegająca  na  zapisaniu  rozwią-

zania konkretnego problemu zazwyczaj w postaci tekstowej, w wybranym języ-
ku programowania, z zachowaniem syntaktyki (składni) oraz semantyki (znacze-
nia poszczególnych symboli oraz ich funkcji w programie) tego języka. 

Komputery moŜna programować w jednym z wielu języków programowa-

nia. Ze względu na poziom abstrakcji, dzieli się je na: 

 

języki wysokiego poziomu, zbliŜone do języka naturalnego, 

 

języki  niskiego  poziomu,  stosowane  do  pisania  programów  bezpośred-
nio w kodzie maszynowym. 

Program, przechowywany w postaci instrukcji binarnych, moŜe być wyko-

nywany  przez  komputer.  Mówi  się,  Ŝe  jest  on  w  postaci  kodu  maszynowego. 
Początkowo  programy  były  pisane  wyłącznie  w  kodzie  maszynowym.  Obecnie 
programy pisze się w językach wysokiego poziomu: CC++, JavaPerlPascal
Program napisany w języku wysokiego poziomu jest tłumaczony na kod maszy-
nowy za pomocą specjalnego programu. 
 

1.1.

 

Kryteria wyboru języka programowania 

NajwaŜniejszym  czynnikiem,  przy  wyborze  pierwszego  języka  do  nauki 

programowania, jest to, na ile uŜyteczny będzie ten język w praktyce, na przy-
kład w przemyśle IT albo w otwartym projekcie. Język C++ lub Java będzie do-
brym  wyborem,  jednak  oprócz  uŜyteczności  i  popularności  są  waŜne  równieŜ 
inne czynniki, na które trzeba zwrócić uwagę. 

Wiele języków programowania jest projektowanych specjalnie po to, by by-

ły  łatwe  do  nauki,  szczególnie  dla  osób  zaczynających  swoją  przygodę  z  pro-
gramowaniem  komputerów. Języki Basic oraz Pascal naleŜą do tej grupy języ-
ków. Są one jednak krytykowane za obniŜoną wydajność, spowodowaną ograni-
czoną  liczbą  udogodnień.  NaleŜy  pamiętać,  Ŝe  podstawową  funkcją  tych  języ-
ków  jest  nauka  programowania  strukturalnego.  Dla  zaawansowanych  aplikacji 
mogą być potrzebne bardziej wyspecjalizowane i kompleksowe języki. 

background image

 

Niektóre  języki  programowania  są  odpowiednie  dla  początkujących,  po-

niewaŜ mają prostą składnię (np. Python), natomiast dydaktycznie mogą spraw-
dzać się języki inne (np. Ada oraz Ruby)

Informacje  o  aktualnej  popularności  języków  programowania  (ranking  ję-

zyków)  moŜna  znaleźć  na  stronie  http://www.tiobe.com/index.php/content/pa-
perinfo/tpci/index.html
.  W  maju  2013  r.  najbardziej  popularne  były  języki: 
C  (18,729 %),  Java  (16,914 %),  Objective-C  (10,428 %),  C++  (9,198 %),  C
(6,119 %),  PHP  (5,784 %),  (Visual)  Basic  (4,656 %),  Python  (4,322 %),  Perl 
(2,276 %), Ruby (1,670 %), JavaScript (1,536 %), Visual Basic .NET (1,131 %), 
Lisp  (0,894 %),  Transact-SQL  (0,819 %),  Pascal  (0,805 %),  Bash  (0,792 %), 
Delphi/Object  Pascal  (0,731 %),  PL/SQL  (0,708 %),  Assembly  (0,638 %),  Lua 
(0,632 %).  W  nawiasach  podano  w  procentach  liczbę  programistów  uŜywają-
cych danego języka. 

Nauka  akademickich języków,  takich jak  Prolog  czy  Smalltalk,  moŜe  wy-

dawać  się  nieprzydatna  w  przyszłej  pracy  zawodowej.  Prostota  tych  języków 
zmusza jednak do zmiany sposobu myślenia o programowaniu i uczy pewnych 
umiejętności, które moŜna potem przenieść na inne języki. Ponadto, znajomość 
wielu  języków  programowania  rozwija  elastyczność  w  myśleniu  i  sprzyja  roz-
wojowi intelektualnemu programisty. 
 

1.2.

 

Efektywność języków niskiego, średniego i wysokiego poziomu 

Jedne  języki  programowania  oferują  duŜą  szybkość  i  sprawność  działania 

programu, natomiast inne są łatwiejsze składniowo. Pisanie w kodzie maszyno-
wym,  najczęściej  w  asemblerze,  moŜe  dać  w  efekcie  najszybszy  i  najbardziej 
spójny  program,  jak  to  tylko  moŜliwe.  Jest  to  jednak  zajęcie  Ŝmudne  i  czaso-
chłonne, gdyŜ asemblery mają bardzo precyzyjną składnię. Pojedyncza instruk-
cja asemblera jest tłumaczona zazwyczaj na jedną lub dwie instrukcje procesora. 

Pojedynczej  instrukcji języka  wysokiego  poziomu  odpowiada  kilka,  kilka-

naście, a czasem nawet kilkadziesiąt instrukcji asemblera, co znacznie przyspie-
sza pisanie programu. Programista musi w tej sytuacji polegać na kompilatorze 
i  ufać,  Ŝe  przekształci  on  tę  instrukcję  w  najefektywniejszą  sekwencję  elemen-
tarnych  instrukcji  procesora.  Eksperymentując  z  opcjami  kompilatora,  moŜna 
uzyskać  kod  maszynowy  szybszy,  ale  o  większym  rozmiarze  albo  wolniejszy, 
ale o mniejszym rozmiarze 

 w zaleŜności od potrzeby. 

Z reguły,  choć  nie  zawsze,  im  łatwiej  zaprogramować  jakieś  zadanie,  tym 

wolniej będzie wykonywał się program realizujący to zadanie. Z tego wynika, Ŝe 
najefektywniejsze  –  pod  względem  jakości  programu  i  czasu  poświęconego  na 
programowanie – są języki średniego poziomu. Na przykład, programy napisane 

background image

 

w  asemblerze  mogą  działać  szybciej  niŜ  programy  napisane  z  wykorzystaniem 
języków średniego poziomu. Z kolei nowocześnie zoptymalizowane kompilato-
ry  dają  często  lepszy  rezultat  niŜ  ten,  jaki  byłby  w  stanie  osiągnąć  przeciętny 
programista. 

W wielu  przypadkach  pisze  się  programy  w  językach  niŜszego  poziomu, 

szczególnie  te,  które  są  składnikami  systemów  operacyjnych.  Na  przykład  Li-
nuks
  i oprogramowanie  GNOME  zostały  napisane  w  C,  poniewaŜ  w  obu  przy-
padkach niezwykle istotna była szybkość działania oprogramowania. Jądro sys-
temu  Linuks  nie  moŜe  działać  zbyt  wolno,  gdyŜ  musi  pracować  równocześnie 
z wieloma programami, nieraz bardzo wymagającymi, zaś oprogramowanie baz 
danych  przetwarza  ogromne  ilości  danych,  i  powolne  jego  działanie  mogłoby 
spowodować niedroŜność systemu. Oprócz tego, programy napisane w językach 
niŜszego  poziomu  mają,  z  reguły,  mniejszy  rozmiar  i  zajmują  mniej  pamięci 
operacyjnej,  co  w  wypadku  jądra  systemu  operacyjnego  jest  o  tyle  istotne, 
Ŝ

e pozostaje więcej zasobów dla innych programów, które dzięki temu działają 

efektywniej. 
 

1.3.

 

Język Pascal w ujęciu historycznym

 

Jeszcze kilkanaście lat temu język Pascal był jednym z najpopularniejszych 

języków  programowania. Jest to język  uniwersalny, wysokiego  poziomu,  ogól-
nego zastosowania, oparty na języku Algol (ang. Algorithmic Language). Pascal 
został opracowany przez Niklausa Wirtha w 1970 roku. Pierwotnie słuŜył celom 
edukacyjnym 

 głównie do nauki programowania strukturalnego. 

Popularność  Pascala  w  Polsce  była  większa  niŜ  w  innych  krajach  ze 

względu na: 

 

dostępność  kompilatorów  w  pirackich  wersjach  (zanim  pojawiło  się 
prawo ochrony własności intelektualnej), 

 

prostotę języka, 

 

popularyzację przez wyŜsze uczelnie. 

Szczyt  popularności  tego  języka  przypadł  na  lata  80.  i  początek  90.  XX 

wieku.  Wraz  ze  zniesieniem  ograniczeń  narzuconych  przez  COCOM  (Komitet 
Koordynacyjny  Wielostronnej  Kontroli  Eksportu
),  upowszechnieniem  się  sieci 
Internet oraz systemów Uniks i Linuks 

 język Pascal został stopniowo wyparty 

przez C i C++. Jedną z popularniejszych implementacji kompilatorów tego języ-
ka  był  produkt  firmy  Borland  International  o  nazwie  Turbo  Pascal.  W  chwili 
obecnej dość mocno rozpowszechnionym obiektowym dialektem języka Pascal 
jest  Object  Pascal,  który  stanowi  podstawę  dla  języków:  Delphi,  Delphi.NET 

background image

10 

 

i OxygenePoza tym, istnieją wolne kompilatory Pascala (np. Free Pascal) oraz 
wolne zintegrowane środowiska programistyczne (np. Lazarus). 

Język  Pascal  bardzo  rygorystycznie  podchodzi  do  kontroli  typów,  tzn. 

sprawdza,  czy  do  zmiennej  typu 

A

  nie  próbuje  się  przypisać  wartości  typu 

B

Jest on zatem językiem silnie typowanym

Popularność Pascala wzrosła z pojawieniem się środowiska programistycz-

nego Delphi, opartego na obiektowym Pascalu, pozwalającego na błyskawiczne 
tworzenie atrakcyjnych wizualnie aplikacji dla systemu operacyjnego Windows
Wraz z pojawieniem się biblioteki Windows dla C++ oraz narzędzi do automa-
tycznego tworzenia graficznego interfejsu uŜytkownika (ang. Graphical User In-
terface 
GUI), Pascal znów stracił na znaczeniu. 

Dla  pewnej  grupy  programistów  niektóre  cechy  Pascala  wykluczają  jego 

zastosowanie w powaŜnych projektach i są powodem krytyki tego języka. We-
dług nich Pascal powinien zostać jedynie narzędziem do nauki programowania. 
W sprzeczności z tymi zarzutami stoi fakt, Ŝe w latach 80. i 90. XX wieku w ję-
zyku tym powstało tysiące aplikacji (równieŜ komercyjnych). 

Zalety  Pascala  (czytelność  kodu  oraz  rygorystyczna  kontrola  typów  da-

nych)  wraz  z  pojawieniem  się  języka  C,  stały  się  dla  programistów  wadami. 
Promowanym przez twórców C standardem stała się natomiast zwięzłość kodu. 

 

1.4.

 

Środowiska programistyczne uŜywające języka Pascal

 

Object  Pascal  jest  to  obiektowy  język  programowania,  stanowiący  obiek-

towe  rozszerzenie  strukturalnego  Pascala.  Object  Pascal jest uŜywany  jako ję-
zyk programowania w środowiskach programistycznych Borland Delphi i Kylix
Object Pascal jest kontynuacją języka Turbo Pascal. Obecnie język ten właści-
wie  jest  nazywany  Delphi  Pascal.  Charakteryzuje  się  głównie  prostą  składnią 
i  bardzo  wysoką  efektywnością  tworzenia  oprogramowania.  Wpływ  na  to  ma, 
przede wszystkim, bardzo szybki kompilator  (kilkakrotnie szybszy, w porówna-
niu do innych języków). 

 
Free Pascal (FPK Pascal albo FPC) jest 32- oraz 64-bitowym kompilato-

rem  języka  Pascal,  dostępnym  na  wiele  róŜnych  platform  sprzętowych  i  syste-
mów operacyjnych. 

Kompilator  Free  Pascal  jest  rozpowszechniany  zgodnie  z  licencją  GPL

Biblioteki  wykonawcze  oraz  dodatkowe  pakiety,  rozpowszechniane  razem  z 
kompilatorem, objęte są jednak zmodyfikowaną licencją LGPL

background image

11 

 

Wizualną  częścią  bibliotek  dla  FPC,  zgodną  z  VCL  (znanym  z  Delphi), 

a takŜe stworzeniem narzędzia typu RAD (ang. Rapid Application Development

 szybkiego tworzenia aplikacji, zajmuje się osobny projekt 

 Lazarus

RAD  jest  to  idea  i  technologia  polegająca  na  udostępnieniu  programiście 

duŜych moŜliwości prototypowania oraz obszernego zestawu gotowych kompo-
nentów (np. zapewniających dostęp do bazy danych). Takie podejście umoŜliwia 
uzyskanie szybkiego efektu juŜ w pierwszych  krokach programistycznych, jed-
nocześnie  stanowi powaŜne  zagroŜenie  dla projektów  o  większych  rozmiarach, 
ze  względu  na  łatwość  nieprzemyślanego  modyfikowania.  Narzędzia  RAD  są 
rozwinięciem  pomysłu  IDE  i  doskonale  nadają  się  do  tworzenia  prototypów. 
Wygląd  aplikacji  projektuje  się rozmieszczając  kontrolki  w  obszarze  okna  pro-
jektowanego programu (np. metodą „przeciągnij i upuść”). 

Przykłady narzędzi typu RAD

 

Microsoft Visual Studio dla Microsoft Windows

 

Delphi firmy Borland oraz Embarcadero

 

Lazarus i Kylix dla GNU/Linuksa

 

Eclipse i NetBeans stworzone dla Javy i posiadające moŜliwość rozsze-
rzenia, w celu obsługi innych języków, 

 

Zend Studio dedykowane dla języka PHP

FPC  posiada  własne  IDE,  stworzone  w  trybie  tekstowym,  jednak  nie  jest 

juŜ ono wspierane. FPC rozpoznaje i kompiluje kod Pascala zapisany w poniŜ-
szych dialektach tego języka: 

 

FPC – domyślny tryb pracy kompilatora, charakteryzujący się moŜliwo-
ś

cią przeciąŜania funkcji i operatorów oraz zagnieŜdŜania komentarzy, 

 

obiektowy FPC – dopuszcza uŜycie rozszerzeń Object Pascala

 

Delphi – zgodność z Delphi w wersji 4, 

 

GPC – zgodność z kompilatorem GPC z pakietu GCC,  

 

TP – zgodność z dialektem uŜytym w kompilatorze Turbo Pascal 7, 

 

GCC (ang. GNU Compiler Collection) to zestaw kompilatorów tworzo-
ny  w  ramach  projektu  GNU.  Początkowo  skrótowiec  GCC  oznaczał 
GNU C Compiler, poniewaŜ był to kompilator tylko języka C. Dostępne 
są kompilatory dla języków: CC++, Objective-CFortranJava (GCJ), 
a takŜe 

 eksperymentalnie 

 AdaPascal i Go. GCC działa na wielu ar-

chitekturach i systemach  operacyjnych,  a  do  operacji  na  plikach  obiek-
towych uŜywa pakietu binutils

 

background image

12 

 

Delphi (Borland Delphi) to zintegrowane środowisko programistyczne typu 

RAD, przeznaczone do pracy pod kontrolą Microsoft Windows, działające zgod-
nie  z  zasadą  dwustronnej  edycji.  Językiem  programowania  (obiektowym),  osa-
dzonym w Delphi, jest Object Pascal

Programy tworzone w Delphi muszą zostać skompilowane do postaci kodu 

binarnego przed pierwszym wykonaniem. Mimo to, niektóre komponenty dzia-
łają juŜ w trakcie tworzenia projektu, umoŜliwiając na bieŜąco śledzenie postę-
pów w pracy nad projektem. 

Delphi  zapamiętuje  informacje  o  właściwościach  obiektów  i  udostępnia je 

programiście.  Informacje  te  umoŜliwiają  programiście  zmianę  ich  wartości  bez 
potrzeby pisania kodu programu oraz są uŜywane w trakcie działania programu. 
Technika ta nosi nazwę RTTI (ang. Run Time Type Information). 

Programy  tworzone  w  Delphi  pracują  na  zasadzie  obsługi  zdarzeń.  KaŜde 

polecenie (np. kliknięcie myszką) generuje zdarzenie, które, poprzez wewnętrz-
ne mechanizmy programu, jest przesyłane do odpowiedniego komponentu. Rola 
programisty ogranicza się tylko do dołączenia odpowiedniego kodu, umoŜliwia-
jącego obsługę tego zdarzenia. 

Cechy i funkcjonalności Delphi

 

szerokie  wspomaganie  obsługi  relacyjnych  systemów  bazodanowych 
(biurkowych oraz SQL-owych), 

 

szeroki zestaw gotowych do uŜycia komponentów, 

 

dwustronna  edycja 

  funkcja  środowiska  programistycznego  odzwier-

ciedlająca  w  kodzie  programu  zmiany  dokonywane  na  jego  zasobach 
(i czasem odwrotnie), 

 

moŜliwość  budowania  wizualnej  części  aplikacji  za  pomocą  techniki 
przeciągnij i upuść” (ang. drag and drop), 

 

szybki i efektywny kompilator języka Object Pascal (do natywnego ko-
du maszynowego), 

 

rozszerzalne środowisko, 

 

dołączone róŜne narzędzia, uzupełniające środowisko Delphi

 

Delphi cieszy się w Polsce stosunkowo duŜą popularnością, w głównej mie-

rze ze względu na relatywną prostotę i powszechność róŜnego rodzaju poradni-
ków dla początkujących. 
 

Embarcadero  Delphi  XE4  jest  rozbudowanym  rozwiązaniem  programi-

stycznym do szybkiego tworzenia prawdziwie natywnych aplikacji dla kompute-

background image

13 

 

rów  PC,  tabletów  oraz  smartfonów.  Natywne  aplikacje  dają  większą  kontrolę, 
większe  bezpieczeństwo  oraz  są  lepiej  dopasowane  do  oczekiwań  uŜytkowni-
ków.  Dzięki  Delphi  XE4  nie  trzeba  prowadzić  jednocześnie  wielu  projektów, 
aby tworzyć natywne aplikacje dla komputerów PC, tabletów i smartfonów, de-
dykowane  róŜnym  platformom  (iOS,  Microsoft  Windows,  Mac  OS).  W  Delphi 
XE4
  moŜna  zrobić  to  w  ramach  pojedynczego  projektu.  Tworzone  aplikacje 
w  pełni  wykorzystują  moŜliwości  i  wydajność  docelowych  urządzeń,  są  wolne 
od języków skryptowych i dodatkowych maszyn wirtualnych. 
 

Kylix  to  zintegrowane  środowisko  dla  programistów,  pracujące  pod  Linuk-

sem, wyprodukowane przez firmę Borland. Pozwalało na pisanie aplikacji w ję-
zyku  Delphi  (Object  Pascal)  i  korzystanie  z  komponentów  CLX.  Od  wersji  3. 
(2003  r.)  Kylix  umoŜliwiało  takŜe  pisanie  aplikacji  w  C++.  Aplikacje  CLX  są 
kompatybilne, na poziomie źródeł, ze środowiskiem Delphi, dzięki czemu zosta-
ło ułatwione przenoszenie tych aplikacji do systemu Windows. MoŜliwe jest tak-
Ŝ

e przenoszenie aplikacji napisanych w Delphi lub w C++ z Windows do Linuk-

sa, ale naleŜy w tym celu skorzystać z komponentów zawartych w międzyplat-
formowej bibliotece programistycznej CLX. Niestety, projekt Kylix został zanie-
chany po wydaniu trzeciej wersji. 

 

Lazarus   to  zintegrowane  środowisko  programistyczne,  oparte  na  kompila-

torze Free Pascal. Jest to, wzorowane na Delphi, wizualne środowisko progra-
mistyczne  oraz  biblioteka  LCL  (ang.  Lazarus  Component  Library),  która  jest 
odpowiednikiem VCL (ang. Visual Component Library). 

Program napisany w środowisku Lazarus moŜna bez Ŝadnych zmian skom-

pilować dla dowolnego obsługiwanego procesora, systemu operacyjnego i inter-
fejsu  okienek.  Lazarus  jest  zgodny  z  Delphi.  Jest  brakującą  częścią  układanki, 
która pozwala na rozwijanie programów, podobnie jak w Delphi, na wszystkich 
platformach obsługiwanych przez FPC

W odróŜnieniu od Javy, która stara się, aby raz napisana aplikacja działała 

wszędzie,  Lazarus i  Free Pascal  starają  się,  aby  raz  napisana aplikacja  kompi-
lowała się wszędzie. PoniewaŜ dostępny jest dokładnie taki sam kompilator, nie 
trzeba wprowadzać zmian, aby otrzymać taki sam produkt dla róŜnych platform. 

Główne cechy Lazarusa

 

program jest udostępniany na licencji GNU GPL, natomiast biblioteki 

 

na zmodyfikowanej licencji LGPL (co oznacza  moŜliwość wykorzysta-
nia Lazarusa w projektach o zamkniętym kodzie), 

 

szybkie przejścia pomiędzy róŜnymi interfejsami i systemami zapewnia-
ją biblioteki The Interface 

 Widget

background image

14 

 

 

Lazarus jest jednym z nielicznych narzędzi, umoŜliwiającym tworzenie 
aplikacji dla urządzeń Pocket PC z Windows CE albo QT Extended

 
Lazarus posiada: 

 

kompilator języka Pascal (Free Pascal), 

 

edytor kodu źródłowego, 

 

metodologię RAD

 

technikę wizualnego tworzenia okien programu (tzw. form), 

 

bibliotekę  RTL  (ang.  Run-Time  Library),  stanowiącą  zestaw  gotowych 
komponentów,  

 

moŜliwość generowania kodu dla wielu: 



 

systemów  operacyjnych  (Win32, Win64, LinuksMac), 



 

platform  sprzętowych  (PentiumPowerPCMac). 

 

1.5.

 

Etapy tworzenia programu komputerowego 

Aby rozwiązać problem, za pomocą programu komputerowego, naleŜy: 

 

określić zadania, które program ma realizować, w celu rozwiązania po-
stawionego  problemu,  z  wyróŜnieniem  informacji  wejściowych  i  wyj-
ś

ciowych 

 zdefiniować problem

 

opracować  kroki  postępowania  prowadzące  do  otrzymania  informacji 
wyjściowej na podstawie informacji wejściowej, czyli wymyślić sposób 
realizacji tych zadań 

 zaprojektować algorytm

 

zapisać  algorytm  w  wybranym  języku  programowania,  według  zasad 
i  symboliki  narzuconej  przez  konkretny  język  programowania  –  zako-
dowa
ć program

 

skompilować i uruchomić program 

 zbudować program

 

sprawdzić, czy program działa poprawnie 

 przetestować program

 

1.6.

 

Cechy dobrego programu komputerowego 

InŜynieria  oprogramowania  koncentruje  się  na  praktycznej  stronie  wytwa-

rzania  oprogramowania  i  obejmuje  róŜne  aspekty  jego  produkcji,  mianowicie: 
analizę i określenie wymagań (specyfikację), projektowanie, wdroŜenie (imple-
mentację),  integrację  poszczególnych  składowych  w  jedną  całość,  modyfikację 
i w końcu dostosowywanie gotowego programu do nowych celów (ewolucję). 

Bez względu na sposób wytwarzania, końcowy program powinien posiadać 

pewne fundamentalne właściwości: 

background image

15 

 

 

niezawodność 

 dobrze zaimplementowane algorytmy, prawidłowe za-

rządzanie  zasobami,  np.  przepełnieniem  bufora,  oraz  brak  błędów  lo-
gicznych typu: dzielenie przez zero, zły warunek krańcowy pętli itp. 

 

solidność 

  umiejętność  radzenia  sobie  programu  z  nieprawidłowymi 

lub  uszkodzonymi  danymi,  a  takŜe  z  niedostępnością  zasobów  takich 
jak: pamięć, usługi systemu operacyjnego, usługi sieciowe itp. 

 

uŜyteczność (ergonomia) 

 łatwość, z jaką uŜytkownik moŜe wykorzy-

stać program, równieŜ do nietypowych zastosowań. UŜyteczność zaleŜy 
od tekstowych i graficznych elementów, które poprawiają przejrzystość, 
intuicyjność, spójność i kompletność interfejsu uŜytkownika. 

 

przenośność 

  róŜnorodność  sprzętu  komputerowego  (platform)  i  sys-

temów operacyjnych, na których program moŜe być uruchamiany. 

 

łatwość konserwacji 

 łatwość, z jaką program moŜe być modyfikowa-

ny,  przez  aktualnych  lub  przyszłych  deweloperów,  w  celu:  zrobienia 
ulepszeń lub dostosowania go do własnych potrzeb, adaptacji do nowe-
go  środowiska  albo  usunięcia  błędów  i  luk  bezpieczeństwa.  Łatwość 
konserwacji  nie  musi  być  bezpośrednio  widoczna  dla  uŜytkownika,  ale 
moŜe znacznie wpłynąć na losy programu w dłuŜszym okresie. 

 

efektywność/wydajność 

  ilość  zasobów  komputera  uŜywanych  przez 

program (im  mniej, tym lepiej): czas procesora, wielkość pamięci, czę-
stość  odczytu  dysku  twardego,  pasmo  danych  karty  sieciowej,  angaŜo-
wanie uwagi uŜytkownika, pozostawianie plików tymczasowych itp. 

 

1.7.

 

Kod źródłowy programu w kontekście języka programowania 

Opracowanie  algorytmu  jeszcze  nie  rozwiązuje  problemu.  Dopiero  zako-

dowanie  go  w  postaci  programu,  w  wybranym  języku,  przybliŜa  nas  do  celu 
(rys. 1.1). PoniewaŜ algorytm wymyśla człowiek, a wykonuje komputer, to za-
chodzi konieczność przetłumaczenia kolejnych kroków algorytmu na postać od-
powiednią (zrozumiałą) do automatycznego wykonywania przez maszynę. 

 

 

Rys. 1.1. Droga od problemu do programu go rozwiązującego 

 

Język  programowania  jest  to  sztuczny  język,  zaprojektowany  do  wydawa-

nia rozkazów maszynie, głównie komputerowi. Jak juŜ wspomniano w akapicie 
drugim na str. 7, opis konkretnego języka programowania jest zwykle dzielony 
na dwa składniki: syntaktykę (formę) oraz semantykę (znaczenie). 

Algorytm 

Kod źródłowy 

Program 

Problem 

background image

16 

 

Większość języków programowania jest czysto tekstowa. Tekst programu  

zawiera:  wyrazy,  liczby  i  znaki  przestankowe.  Istnieją  teŜ  języki  programo-
wania, które są w swej naturze bardziej graficzne, a do zdefiniowania programu 
stosuje się w nich relacje między symbolami graficznymi. 

Tworzenia  programów,  za  pomocą  języków  programowania,  przypomina 

układanie prostych zdań w języku naturalnym, zawierających wyraŜenia arytme-
tyczne znane w matematyce. Programy pisze się w specjalnym edytorze tekstu. 
W ten sposób powstaje tekst źródłowy, zwany teŜ kodem źródłowym programu

Syntaktyka języka opisuje dopuszczalne kombinacje symboli, które tworzą 

składniowo  poprawny  program.  Znaczenie  konkretnej  kombinacji  symboli  roz-
poznawane  jest  przez  semantykę.  Nie  wszystkie  programy  poprawne  syntak-
tycznie  są  poprawne  semantycznie.  Z  semantyką  związany  jest  system  typów, 
który  definiuje,  jak  język  programowania  klasyfikuje  wartości  i  wyraŜenia,  jak 
moŜe nimi manipulować i w jaki sposób mogą one wchodzić w interakcje. 

W  statycznym  typowaniu  wszystkie  wyraŜenia  mają  przypisane  typy  na 

etapie  kompilacji,  zanim  program  zostanie  wykonany.  Statycznie  typowane  ję-
zyki  mogą  być  typowane  jawnie  albo  wnioskująco.  W  pierwszym  przypadku 
programista musi zapisać typy w sposób jawny, w określonych miejscach w tek-
ś

cie programu (np. podczas deklaracji zmiennych). W drugim przypadku kompi-

lator wnioskuje typy wyraŜeń i deklaracji w oparciu o kontekst, w  którym  wy-
stępują. Popularnymi językami, w których manifestuje się typy są: C++, C#, Ja-
va
. Pełne wnioskowanie o typach występuje w mniej znanych językach progra-
mowania: Haskell i ML

Dynamiczne  typowanie,  zwane  teŜ  późnym,  określa bezpieczeństwo  ope-

racji  w  trakcie  jej  wykonywania,  tzn.  typy  są  przypisywane  wartościom  otrzy-
mywanym  w  trakcie  działania  programu,  a  nie  tekstowym  wyraŜeniom.  Dyna-
micznie typowane języki nie wymagają od programisty wyraźnego (precyzyjne-
go) podania typu wyraŜenia. Dzięki temu pojedyncza zmienna moŜe odnosić się 
do  wartości  o  róŜnych  typach  w  róŜnych  punktach  wykonywania  programu. 
Z tego powodu błędy związane z typami nie mogą być wykrywane automatycz-
nie, aŜ do momentu, gdy odpowiedni fragment programu jest rzeczywiście wy-
konywany, potencjalnie czyniąc debugowanie bardziej złoŜonym. Dynamicznie 
typowanymi językami są: LispPerlPythonJavaScript i Ruby

Słabe typowanie dopuszcza traktowanie wartości jednego typu jako warto-

ś

ci innego typu. To moŜe być okazjonalnie uŜyteczne, ale moŜe teŜ powodować 

pewnego rodzaju błędy programu, niewykrywalne na etapie kompilacji, a nawet 
podczas uruchamiania. 

background image

17 

 

Silne typowanie nie ma wad typowania słabego. Próba wykonania operacji 

na wartości o niewłaściwym typie generuje błąd juŜ na etapie kompilacji. Silnie 
typowane języki są często określane mianem bezpiecznych (ang. type-safe). 

Większość języków programowania posiada powiązaną bibliotekę standar-

dową,  która  jest  udostępniana  wszystkim  implementacjom  tego  języka.  Biblio-
teka  standardowa
,  zwana  teŜ  rdzeniową,  zawiera  definicje  powszechnie  uŜy-
wanych  algorytmów,  struktur  danych  oraz  mechanizmów  wejścia-wyjścia.  Bi-
blioteka  rdzeniowa  jest,  przez  jej  uŜytkowników,  często  traktowana  jako  część 
języka. Projektanci tej biblioteki mogą jednak traktować ją jako oddzielny byt. 

Po  napisaniu  programu,  tekst  źródłowy  poddawany  jest  kompilacji  przez 

program zwany kompilatorem. Następnie, w fazie łączenia, dokonywane jest po-
łączenie procedur bibliotecznych z tekstem programu. Zadanie to realizuje linker 
lub konsolidator

Efektem końcowym przetworzenia kodu źródłowego jest program wyniko-

wy,  który  moŜe  być  zapisany  na  dysku  komputera  i  później  wielokrotnie  uru-
chamiany za pośrednictwem systemu operacyjnego. 

 

1.8.

 

Rola kompilatora w procesie powstawania programu 

Kompilacja,  w  programowaniu,  to  przetłumaczenie  wersji  źródłowej  pro-

gramu  na  język  maszyny  (procesora).  Kompilacji  wersji  źródłowej  programu 
dokonuje kompilator danego języka programowania. 

Kompilator  jest  programem  komputerowym  lub  zestawem  programów, 

który  tłumaczy  kod  źródłowy,  napisany  w  języku  programowania  (w  języku 
ź

ródłowym), na inny język komputerowy 

 język docelowy, posiadający zwykle 

formę binarną, zwaną kodem obiektowym. Powodem, dla którego tłumaczy się 
kod źródłowy jest utworzenie wykonywalnego programu (rys. 1.2). Kod obiek-
towy,  zwany  teŜ  modułowym,  jest  sekwencję  wyraŜeń  lub  instrukcji  w  języku 
kodu maszyny (w postaci zer i jedynek). 

 

 

Rys. 1.2. Droga od kodu źródłowego do wykonywalnego programu 

 

Tekst źródłowy 

programu 

Reprezentacja 

pośrednia 

Program 

wykonywalny 

kompilacja 

konsolidacja 

Biblioteki stan-

dardowe i inne 

background image

18 

 

Kompilator  wykonuje  większość  lub  wszystkie  następujące  czynności: 

analizę leksykalną, preprocessing, parsowanie, analizę semantyczną,  generowa-
nie kodu, optymalizację kodu. 

Nazwa kompilator, przede wszystkim, jest uŜywana w odniesieniu do pro-

gramów,  które tłumaczą kod źródłowy z języka programowania wysokiego po-
ziomu  do  języka  niskiego  poziomu  (kodu  asemblera  lub  kodu  maszynowego). 
Kompilatory umoŜliwiają tworzenie programów niezaleŜnych od maszyny. 

Błędne działanie programu, spowodowane nieprawidłową pracą kompilato-

ra,  jest  bardzo  trudne  do  wykrycia,  dlatego  twórcy  kompilatorów  dokładają 
wszelkich starań, aby zapewnić wysoką jakość tego oprogramowania. 

 

1.9.

 

Architektura kompilatora 

KaŜdy kompilator składa się z trzech głównych części: 

 

front-end 

 sprawdza, czy  program uŜytkownika jest napisany popraw-

nie pod względem składniowym i znaczeniowym. Na tym etapie rapor-
towane  są  błędy,  jeŜeli  istnieją  i  wykonywana  jest  kontrola  typów. 
Front-end  generuje  pośrednią  reprezentację  (ang.  Intermediate  Repre-
sentation
 – IR) kodu źródłowego, która jest przetwarzana w następnym 
etapie. Front-end zarządza tablicą symboli, tzn. strukturą danych, która 
dla kaŜdego symbolu występującego w kodzie źródłowym przechowuje 
informacje  o  jego  połoŜeniu,  typie  i  zasięgu.  Leksykalna  analiza  dzieli 
tekst źródłowy na małe kawałki (tokeny). KaŜdy token jest pojedynczym 
elementem języka programowania, takim jak: słowo kluczowe, identyfi-
kator,  nazwa  symbolu  itp.  Składnia  tokena  jest  zwykle  językiem  regu-
larnym, więc do rozpoznania tokena moŜna wykorzystać automat skoń-
czony,  skonstruowany  z  wyraŜeń  regularnych.  Niektóre  języki  (np.  C
wymagają  fazy  preprocessingu,  obsługującej  podstawienia  makr  oraz 
kompilację warunkową. Faza ta występuje przed analizą syntaktyczną i 
semantyczną.  Analiza  syntaktyczna  obejmuje  parsowanie sekwencji  to-
kenów  w  celu  wyznaczenia  syntaktycznej  struktury  programu.  Ta  faza 
buduje drzewo parsowania, która zastępuje liniową sekwencję tokenów 
strukturą  drzewiastą,  zgodnie  z  zasadami  gramatyki  formalnej,  definiu-
jącej  składnię  języka  programowania.  Drzewo  parsowania  jest  analizo-
wane  i  transformowane  w  późniejszych  fazach  kompilacji.  W  czasie 
analizy  semantycznej  kompilator  dodaje  informację  znaczeniową  do 
drzewa parsowania i buduje tablicę symboli. RównieŜ wtedy odbywa się 
semantyczne sprawdzanie takich rzeczy jak: typy (na obecność błędów), 
wiązanie  obiektów  (kojarzenie  zmiennych  i  referencji  do  funkcji  z  ich 
definicjami),  definitywne  przypisanie  (wymaganie  inicjalizacji  wszyst-

background image

19 

 

kich  zmiennych  lokalnych przed  ich  uŜyciem).  To  sprawdzanie  kończy 
się odrzuceniem źródła programu lub wygenerowaniem ostrzeŜeń. 

 

middle-end 

 na tym etapie są dokonywane optymalizacje niezaleŜne od 

kodów: źródłowego i maszynowego. Kod pośredni jest transformowany 
do  równowaŜnej  funkcjonalnie,  ale  szybszej  lub  mniejszej  formy.  Ty-
powe transformacje, którym poddawany jest kod IR, w celu optymaliza-
cji,  to:  usunięcie  nieuŜywanego  lub  nieosiągalnego  kodu,  wykrycie  i 
propagacja  wartości  stałych,  relokacja  obliczeń  do  rzadziej  wykonywa-
nych  miejsc,  specjalizacja obliczeń  opartych  na  kontekście,  wstawienie 
kodu typu inline. Tak zoptymalizowany kod moŜe być dzielony między 
róŜnymi wersjami kompilatora, obsługującymi róŜne języki i procesory 
docelowe.  Z  powodu  dodatkowego  czasu  i  dodatkowej  przestrzeni,  po-
trzebnych  na  analizę  i  optymalizację,  niektóre  kompilatory  domyślnie 
opuszczają  ją.  UŜytkownik  musi  wtedy  sam  wyraźnie  „powiedzieć” 
kompilatorowi, które optymalizacje chce włączyć. 

 

back-end 

 jest odpowiedzialny za tłumaczenie kodu pośredniego, uzy-

skanego w fazie middle-end, na kod asemblera. Na tym etapie odbywają 
się analizy, transformacje i optymalizacje przeznaczone dla określonego 
typu komputera 

 generowany jest kod dla konkretnego procesora i sys-

temu  operacyjnego.  Instrukcje  docelowe  asemblera  są  dobierane  dla 
kaŜdej instrukcji kodu pośredniego. Niektóre zmienne programu są alo-
kowane  w  rejestrach  procesora.  Generowane  są  takŜe  dane  ułatwiające 
debugowanie.  Back-end  wykorzystuje  architekturę  procesora  w  celu 
rozdziału pracy między jednostki wykonawcze procesora, wypełnia slo-
ty opóźniające itp. 

 

Przedstawiony podział kompilatora na 3 części pozwala łączyć front-end-y 

róŜnych języków z back-end-ami róŜnych CPU. Praktycznym przykładem takie-
go  podejścia  są:  GNU  Compiler  Collection,  LLVM  oraz  Amsterdam  Compiler 
Kit
, które mają wiele front-end-ów, wspólną analizę oraz wiele back-end-ów. 

 

1.10.

 

Podział kompilatorów ze względu na platformę 

Jedna  z  klasyfikacji  kompilatorów  dzieli  je  ze  względu  na  platformę, 

na  której  będzie  wykonywany  kod  przez  dany  kompilator  wygenerowany.  Tę 
platformę  nazywa  się  docelową.  Natywny  lub  hostowany  kompilator  to  taki, 
którego wynik działania przeznaczony jest do uruchamiania na takim samym ty-
pie komputera i systemu operacyjnego, na jakim uruchamiano kompilator. 

background image

20 

 

JeŜeli  skompilowany  program  daje  się  uruchomić  na  komputerze  z  CPU 

i  z  systemem  operacyjnym  innymi  od  tych,  na  których  kompilator  był  urucha-
miany, to do kompilacji został uŜyty kompilator skrośny (ang. cross-compiler).  

Efekt  działania  kompilatora  skrośnego  jest  przeznaczony  do  uruchamiania 

na  innej  platformie.  Kompilatory  skrośne  są  często  stosowane  do  wytwarzania 
oprogramowania  dla  systemów  wbudowanych  (ang.  embedded  systems),  które 
w zamierzeniu nie będą obsługiwać środowisk wytwarzania oprogramowania. 

Efekt działania kompilatorów, które produkują kod dla wirtualnej maszyny 

(ang.  Virtual Machine  =  VM),  moŜe  być  lub  nie być  uruchamiany  na  tej  samej 
platformie, na której kompilator wyprodukował kod. 

Języki wyŜszego poziomu zwykle pojawiają się 

 w załoŜeniu 

 z pewnym 

rodzajem  translacji.  Są  one  projektowane  albo  jako  języki  kompilowane,  albo 
jako  interpretowane.  Interpretacja  niezupełnie  zastępuje  kompilację.  Interpreta-
cja  jedynie  ukrywa  kompilację  przed  uŜytkownikiem  i  czyni  ją  bardziej  stop-
niowaną. Specyfikacje niektórych języków wyraźnie mówią, Ŝe ich implementa-
cje muszą posiadać moŜliwość kompilacji. Tak jest np. z Common Lisp, jednak 
nie  ma  niczego  szczególnego,  tkwiącego  w  definicji  Common  Lisp,  co  by  blo-
kowało  go  przed  byciem  interpretowanym.  Nowoczesne  trendy  w  kierunku 
kompilacji  typu  just-in-time  oraz  interpretacji  bajtkodu  rozmywają  tradycyjny 
podział kompilatorów i interpretatorów na kategorie. 

Kompilator,  dla  względnie  prostego  języka,  napisany  przez  jedną  osobę, 

moŜe  mieć  postać  pojedynczego,  monolitycznego  kawałka  oprogramowania. 
Gdy  język  źródłowy  jest  obszerny  i  skomplikowany,  i  wymagany  jest  wynik  o 
wysokiej  jakości,  wówczas  projekt  kompilatora  moŜe  być  podzielony  na  kilka 
niezaleŜnych faz, tzn. na małe fragmenty,  które mogą być przekazane do opra-
cowania  róŜnym  osobom.  Dzięki  temu  łatwo  zastąpić  jakąś  fazę  jej  ulepszoną 
wersją  lub  wprowadzić  zupełnie  nową  fazę  na  późniejszym  etapie.  Wszystkie, 
nawet najmniejsze kompilatory, posiadają więcej niŜ dwie fazy. Czasem trudno 
wskazać miejsce, w którym fazy front-end i back-end łączą się ze sobą. 

 

1.11.

 

Podział kompilatorów ze względu na liczbę przejść 

Podział kompilatorów, ze względu na liczbę przejść (ang. pass), ma swoje 

podłoŜe  w  ograniczeniach  sprzętu  komputerowego.  Kompilacja  wymaga  prze-
prowadzenia  mnóstwa  działań,  a  wczesne  komputery  nie  miały  wystarczająco 
pamięci,  aby  pomieścić jeden  program,  który  zrobiłby  to  wszystko.  Z  tego  po-
wodu kompilatory były dzielone na mniejsze programy, które robiły przejścia na 
kodzie źródłowym lub na jego reprezentacji, wykonując jedną analizę lub trans-
lację.  Umiejętność  kompilacji  w  jednym  przejściu  (ang.  single  pass)  uwaŜana 

background image

21 

 

jest za zaletę, gdyŜ upraszcza pisanie kompilatora, a poza tym kompilatory tego 
typu (ang. one-pass) wykonują kompilację szybciej niŜ kompilatory multi-pass
Tak więc, z powodu ograniczonych zasobów pierwszych systemów komputero-
wych, wiele wczesnych języków programowania było projektowanych tak, aby 
mogły być kompilowane w jednym przejściu (np. Pascal). 

W  pewnych  przypadkach  zaprojektowana  cecha  języka  programowania 

moŜe  wymagać  więcej  niŜ  jednego  przejścia  nad  kodem  źródłowym.  Wadą 
kompilacji  w  jednym  przejściu  jest  to,  Ŝe  uniemoŜliwia  ona  przeprowadzenie 
wielu  wyrafinowanych  optymalizacji,  potrzebnych  do  wygenerowania  kodu  o 
wysokiej  jakości.  Czasami  trudno  dokładnie  obliczyć,  ile  przejść  wykonuje 
kompilator  optymalizujący.  Na  przykład,  róŜne  fazy  optymalizacji  mogą  anali-
zować jedno wyraŜenie wiele razy, a inne 

 tylko jeden raz. Typowy kompilator 

o wielu przejściach zwraca ostatecznie kod maszynowy. Są jednak inne rodzaje 
kompilatora multi-pass, mianowicie: 

 

source-to-source 

 kompiluje język wysokiego poziomu do tego samego 

lub innego języka wysokiego poziomu. Na przykład, kompilator do au-
tomatycznego  zrównoleglania  kodu  moŜe  do  kodu  wysokiego  poziomu 
dopisywać wstawki w tym samym języku wysokiego poziomu, określa-
jące jak prowadzić obliczenia równoległe. 

 

stage compiler 

 kompiluje kod źródłowy do kodu asemblera teoretycz-

nej  maszyn  (np.  maszyny  języka  Prolog 

  Warren  Abstract  Machine). 

Kompilatory  bajtkodu  dla  języków  Java  i  Pyton  są  odmianami  stage 
kompilatora. 

 

just-in-time compiler 

 w przypadku tego kompilatora, aplikacje są do-

starczane w postaci bajtkodu, który następnie jest kompilowany do kodu 
natywnej maszyny, tuŜ przed wykonaniem. Ten typ kompilatora uŜywa-
ny jest m.in. przez systemy: SmalltalkJava oraz Microsoft .NET Com-
mon Intermediate Language
 (CIL). 

 

Skompilowany kod źródłowy, zazwyczaj zapisywany w pliku na dysku ja-

ko  tzw.  plik  obiektowy 

  object  file, jest juŜ  w  języku  maszynowym,  ale  musi 

jeszcze  zostać  połączony  z  bibliotekami.  Biblioteki  zawierają  dodatkowe  in-
strukcje  programu,  napisane  wcześniej  przez  tego  samego  programistę  albo 
przez kogoś innego, i oddzielnie skompilowane. Łączenie skompilowanego ko-
du  źródłowego  z  bibliotekami  statycznymi  jest  wykonywane  przez  program 
zwany linkerem (ang. link 

 łączyć), na etapie linkowania, określanego teŜ mia-

nem konsolidacji. W ten sposób otrzymujemy program  gotowy do uruchomie-
nia (plik wykonywalny). Dodatkowo, podczas konsolidacji, do pliku wynikowe-
go  mogą  być  dołączone  odpowiednie  nagłówki  i  informacje  charakterystyczne 
dla konkretnego formatu pliku wykonywalnego. 

background image

22 

 

2.

 

Algorytmy i schematy blokowe 

 

Napisanie  programu  musi  być  poprzedzone  opracowaniem  odpowiedniego 

algorytmu, tzn. przepisu na rozwiązanie konkretnego problemu. 

Algorytmy moŜna zapisywać na róŜne sposoby: 

 

w języku naturalnym, 

 

w pseudokodzie (w języku zbliŜonym do naturalnego), 

 

w postaci schematu blokowego, 

 

w postaci prezentacji multimedialnej, 

 

w postaci instrukcji programu. 

 

2.1.

 

Podstawowe cechy algorytmów

 

RozwaŜmy  pewien  zestaw  danych,  np.  {a1,  a2,  a3}  i  zdefiniujmy  na  nim 

pewną operację (np. 

), która wygeneruje wyniki {xyz}. Przez algorytm bę-

dziemy  w  tym  przypadku  rozumieć  zestaw  czynności  (obliczeń),  które  naleŜy 
wykonać, aby otrzymać dane wyjściowe z danych wejściowych. 

Ogólnie,  algorytm  to  zbiór  reguł  postępowania,  mający  na  celu  prze-

tworzenie informacji wejściowych w informacje wyjściowe. Informacje wej-
ś

ciowe zazwyczaj są nazywane danymi, a informacje wyjściowe 

 wynikami. 

Algorytm  musi  posiadać  skończoną  liczbę  reguł  postępowania  i  moŜ

zawierać tylko pewien skończony zbiór czynności (instrukcji). 

Algorytm  opracowuje  się  dla  rozwiązywania  problemów  o  powtarzalności 

metod  wnioskowania  i  dla  róŜnych  wejść.  Oznacza  to,  Ŝe  algorytm  słuŜy  do 
rozwiązywania problemów tej samej klasy. Wykorzystywane dane powinny być 
sparametryzowane, tzn. nie powinno się uŜywać wielkości stałych, ale pewnych 
symboli, reprezentujących te dane, np.:  a = 1, b = 2, x = a

Algorytm  projektuje  się  dla  zadań,  dla  których  istnieje  rozwiązanie. 

W  przypadku  gdy  trudno jest udowodnić istnienie  rozwiązania,  naleŜy  określić 
moment przerwania wykonywania zbioru reguł. 

Algorytm powinien uwzględniać wszystkie moŜliwe sytuacje, jakie mo-

gą  wystąpić  podczas  rozwiązywania  zadania.  Przykładowo,  podczas  rozwią-

zywania  równania 

0

2

=

+

+

c

x

b

x

a

  naleŜy  przewidzieć  równieŜ  okoliczność, 

kiedy nie posiada ono rzeczywistych pierwiastków. Ponadto, dla 

0

=

a

 nie jest 

to równanie kwadratowe, gdyŜ wtedy degeneruje się ono do równania liniowe-

background image

23 

 

goPoza tym, dla 

,

0

=

a

 

0

=

b

 i 

0

c

 równanie jest sprzeczne, natomiast gdy 

,

0

=

a

 

0

=

b

 i 

0

=

c

 równanie posiada nieskończenie wiele rozwiązań. 

Algorytm  moŜe  być  realizowany  przez  człowieka  albo  przez  maszynę. 

W  przypadku  komputera  to  procesor  wykonuje  operacje  arytmetyczne,  czyli: 
dodawanie, odejmowanie, mnoŜenie i dzielenie. Budowa procesora determinuje, 
w pewnym zakresie, zbiór operacji, które moŜna zastosować podczas rozwiązy-
wania danego zadania. Przykładami algorytmów są: 

 

przepis na przygotowanie jakiegoś dania, 

 

instrukcja składania komputera lub samochodu, 

 

sposób rozwiązywania równania kwadratowego, 

 

metoda obliczania wyznacznika macierzy itp. 

 

2.2.

 

Klasyfikacja algorytmów 

ze względu na sposób implementacji 

Istnieją róŜne kryteria podziału algorytmów na klasy. Ze względu na sposób 

implementacji, algorytmy dzieli się na: 

 

rekurencyjne (rekursywne) lub iteracyjne, 

 

szeregowe, równoległe lub rozproszone, 

 

deterministyczne lub niedeterministyczne, 

 

dokładne lub przybliŜone. 

Algorytm rekurencyjny to taki, który wywołuje siebie (odnosi się do sie-

bie) do momentu, aŜ pewien warunek zostanie spełniony. Algorytm iteracyjny
do  rozwiązania  problemu,  uŜywa  powtarzalnych  konstrukcji,  takich  jak  pętle. 
Algorytm ten kończy działanie, gdy pętla wykona się określoną liczbę razy albo 
gdy spełniony zostanie warunek przerwania tej pętli. KaŜdy algorytm rekuren-
cyjny posiada, mniej lub bardziej skomplikowan
ą, równowaŜną wersję ite-
racyjn
ą, i odwrotnie. Przekształcenie algorytmu rekurencyjnego, w odpowiada-
jący mu funkcjonalnie algorytm iteracyjny, nazywane jest derekursywacją. Cho-
ciaŜ  algorytmy  rekurencyjne  bywają  prostsze  do  zrozumienia,  to  są  obciąŜone 
duŜą  złoŜonością  obliczeniową  i  pamięciową  (kosztowna  obsługa  stosu  wywo-
łań). Algorytmy iteracyjne są bliŜsze architekturze procesorów i dlatego wyko-
nują się szybciej oraz zuŜywają mniej pamięci operacyjnej. 

Algorytmy  są  zazwyczaj  dyskutowane  przy  załoŜeniu,  Ŝe  komputer  moŜe 

wykonać jedną instrukcję w danym momencie. Algorytm projektowany do dzia-
łania w taki sposób nosi nazwę algorytmu szeregowegoAlgorytmy równole-
głe
 wykorzystują natomiast zaletę architektury niektórych komputerów, polega-

background image

24 

 

jącą na moŜliwości „pracowania” kilku procesorów równocześnie nad rozwiąza-
niem danego problemu. Algorytmy rozproszone wykorzystują wiele kompute-
rów połączonych w sieć. Algorytmy równoległe i rozproszone dzielą problem na 
mniej lub bardziej symetryczne podproblemy, by następnie zebrać te częściowe 
wyniki z powrotem w całość. Algorytmy te zuŜywają czas nie tylko na oblicze-
nia,  ale  równieŜ  na  komunikację  między  procesorami.  Na  przykład,  algorytmy 
sortowania moŜna efektywnie zamieniać na równoległe, ale proces komunikacji 
jest zbyt czasochłonny. Iteracyjne algorytmy, w większości przypadków, moŜna 
konwertować na równoległe. Niektóre algorytmy nie dają się zrównoleglić. 

Algorytm  deterministyczny  rozwiązuje  problem  podając  dokładnie  okre-

ś

lony  rezultat  na  kaŜdym  etapie.  Działanie tego  algorytmu  jest  całkowicie  zde-

terminowane  przez  warunki  początkowe.  Kilkukrotne  uruchomienie  algorytmu 
deterministycznego doprowadzi za kaŜdym razem do takiego samego wyniku. 

Algorytm  niedeterministyczny  rozwiązuje  zadanie  poprzez  zgadywanie, 

chociaŜ tę metodę prób i błędów moŜna uczynić bardziej skuteczną, stosując od-
powiednie  heurystyki,  czyli  oparte  na  doświadczeniu  i  intuicji  techniki  zgady-
wania,  przewidywania  itp.  Podczas  gdy  wiele  algorytmów  zwraca  dokładne 
rozwiązanie, algorytmy niedeterministyczne szukają aproksymowanego rozwią-
zania, które jest bliskie dokładnemu. Proces aproksymacji moŜe stosować strate-
gię  deterministyczną  albo  losową.  Algorytmy  przybliŜone  mają  praktyczne 
znacznie podczas rozwiązywania wyjątkowo trudnych problemów. 

 

2.3.

 

Klasyfikacja algorytmów 

ze względu na sposób zaprojektowania 

Ze  względu  na  sposób  zaprojektowania  lub  paradygmat  moŜna  wyróŜnić 

następujące typy algorytmów

 

brutalnej  siły”  (ang.  brute-force)  lub  wyczerpującego  (gruntownego) 
przeszukiwania (ang. exhaustive search), 

 

dziel i zwycięŜaj” (ang. divide and conquer), 

 

dynamicznego programowania (ang. dynamic programming), 

 

zachłanny (ang. greedy method), 

 

programowania liniowego (ang. linear programming), 

 

przeszukiwania i przeliczania (ang. search and enumeration). 

 

Algorytm  typu  „brutalnej siły  przegląda  (próbuje)  wszystkie  moŜliwe 

rozwiązania w celu znalezienia tego najlepszego. 

Algorytm  typu  „dziel i  zwycięŜaj,  w sposób  powtarzalny,  redukuje  dany 

problem (zazwyczaj poprzez zastosowanie rekurencji) do jednej lub więcej, ale 

background image

25 

 

mniejszych  instancji  tego  samego  problemu  do  momentu,  aŜ  te  instancje  są  na 
tyle małe, Ŝe moŜna je łatwo rozwiązać. 

JeŜeli  problem  zawiera  optymalne  substruktury  oraz  nakładające  się  pod-

problemy, wówczas warto zastosować podejście zwane dynamicznym progra-
mowaniem
.  Pozwala  ono  uniknąć  ponownego  obliczania  rozwiązań  częścio-
wych, wyznaczonych dotychczas. Problem zawiera optymalne substruktury wte-
dy,  gdy  optymalne  rozwiązanie  całego  problemu  moŜe  być  skonstruowane 
z optymalnych rozwiązań  podproblemów. Problem wykazuje posiadanie nakła-
dających  się  podproblemów  wtedy,  gdy  te  same  podproblemy  są  uŜywane  do 
rozwiązania wielu róŜnych instancji danego problemu. 

Algorytm  zachłanny  jest  podobny  do  programowania  liniowego,  z  tym 

wyjątkiem, Ŝe rozwiązania podproblemów nie muszą być znane na kaŜdym eta-
pie. Zamiast tego algorytm zachłanny wybiera takie rozwiązanie, które jest op-
tymalne na danym etapie. Algorytm zachłanny rozszerza rozwiązanie za pomocą 
najlepszej przewidywanej decyzji na bieŜącym etapie i najlepszej decyzji podję-
tej  na  poprzednim  etapie.  Takie  postępowanie  nie  jest  wyczerpujące  (nie  prze-
szukuje wszystkich sytuacji) i w wielu przypadkach nie daje prawidłowych (do-
kładnych) rozwiązań problemów. Algorytmami tego typu są np. algorytmy wy-
znaczania minimalnego drzewa rozpinającego: Prima, Kruskala oraz Sollina. 

Kiedy do rozwiązania problemu stosuje się programowanie liniowe, wtedy 

określa się pewne nierówności na danych wejściowych, a następnie próbuje się 
wyznaczyć maksimum lub minimum pewnej funkcji liniowej, zdefiniowanej dla 
danych  wejściowych.  Rodzajowym  algorytmem  programowania  liniowego  jest 
metoda  simpleks.  Bardziej  skomplikowaną  odmianą  programowania  liniowego 
jest programowanie całkowitoliczbowe, z którym mamy do czynienia wówczas, 
gdy przestrzeń dopuszczalnych rozwiązań jest ograniczona do liczb całkowitych.  

Algorytm  przeszukujący  stara  się  znaleźć  element  o  określonych  właści-

wościach w zbiorze innych elementów. Do wirtualnych przestrzeni wyszukiwa-
nia  stosuje  się  metody:  przeszukiwania  lokalnego,  drzewa  przeszukiwań  oraz 
drzewa gry. Metaheurystyczne metody przeszukiwania lokalnego to: symulowa-
ne wy
Ŝarzanieprzeszukiwanie tabu oraz programowanie genetyczne. Do metod 
drzewa przeszukiwań zalicza się algorytmy: przeszukiwania wgłąb (ang. depth- 
first search
 = DFS), przeszukiwania wszerz (ang. breadth-first search = BFS), 
róŜne  techniki  przycinania  drzewa,  przeszukiwania  z  powrotami  (ang.  back-
tracking
), a takŜe technikę podziału i ograniczeń BB (ang. branch and bound). 
Do przeszukiwania drzew gier, takich jak szachy czy warcaby, stosuje się algo-
rytmy minimax albo alpha-beta, w róŜnych odmianach. 

 

background image

26 

 

KaŜda dziedzina nauki ma swoje problemy do rozwiązania i wymaga efek-

tywnych algorytmów. Podobne zagadnienia z jednej dziedziny są często dysku-
towane wspólnie. Algorytmy  z róŜnych dziedzin niejednokrotnie przenikają się 
i są ze sobą powiązane. Ze względu na obszar zastosowań, algorytmy dzieli się 
m.in. na: algorytmy przeszukujące, sortujące, numeryczne, grafowe, kombinato-
ryczne,  działające  na  łańcuchach,  kryptograficzne,  związane  z  kompresją  da-
nych, sztuczną inteligencją i uczeniem maszynowym. 

Algorytmy są takŜe klasyfikowane ze względu na czas potrzebny do ich za-

kończenia w funkcji rozmiaru danych wejściowych. WyróŜniamy tutaj algoryt-
my o złoŜoności liniowej, kwadratowej, logarytmicznej, a nawet wykładniczej. 

ZłoŜoność czasowa algorytmu jest miarą ilości czasu potrzebnego do wy-

konania algorytmu w funkcji rozmiaru danych wejściowych (w funkcji długości 
ciągu  danych  wejściowych).  ZłoŜoność  czasowa jest zwykle  określana  poprzez 
obliczenie  liczby  elementarnych  operacji  wykonywanych  przez  algorytm.  Po-
niewaŜ  potrzeba  stałego  czasu  na  wykonanie  kaŜdej  elementarnej  operacji,  to 
czas  wykonania  algorytmu  i  liczba  elementarnych  operacji  są  do  siebie  wprost 
proporcjonalne,  tzn.  róŜnią  się  o  co  najwyŜej  stały  współczynnik.  ZłoŜoność 
czasową  algorytmów  wyraŜa  się  za  pomocą  notacji  „duŜe  O”,  która  opuszcza 
składniki niŜszego rzędu. Mówi się, Ŝe tak zdefiniowana złoŜoność czasowa jest 
opisana  asymptotycznie,  tzn.  gdy  rozmiar  danych  wejściowych  rośnie  do  nie-
skończoności. 
 

2.4.

 

Właściwości schematów blokowych 

Algorytm  jest  często  przedstawiany  za  pomocą  graficznej  reprezentacji 

z wykorzystaniem symboli graficznych. Ta reprezentacja nosi nazwę schematu 
blokowego 
lub sieci działań. 

Schemat  blokowy  jest  poglądową  formą  graficznego  przedstawienia  algo-

rytmu.  Tworzy  się  go  korzystając  ze  ściśle  określonego  zbioru  figur  geome-
trycznych  oraz  stosując  ustalone  reguły  ich  łączenia.  We  wnętrzu  bloków, 
w umowny sposób, zapisuje się występujące w algorytmie operacje arytmetycz-
ne, logiczne, operacje wejścia i wyjścia oraz warunki, od których zaleŜą decyzje, 
co do kolejności wykonywania obliczeń. 

Zaletą  schematów  blokowych  jest  to,  Ŝe  graficznie  reprezentują  algorytm 

zarówno  ze  względu  na  typy  występujących  w  nim  działań,  jak  i  na  ich  kolej-
ność.  KaŜdy  schemat  blokowy  musi  być  spójny,  tzn.  od  bloku  start  do  bloku 
stop musi prowadzić przynajmniej jedna droga. 

 

 

background image

27 

 

Tabela 2.1. Symbole stosowane w schematach blokowych 

Nazwa bloku 

Symbol 

Znaczenie 

STRZAŁKA 

 

Określa kierunek (drogę) przepływu danych 

lub kolejność wykonywania działań. 

START 

 

Od tego bloku rozpoczyna się wykonywanie 

algorytmu. Występuje tylko raz w schemacie. 

Wychodzi z niego tylko jedna strzałka. 

STOP 

 

Na tym bloku kończy się wykonywanie algo-

rytmu. Wchodzi do niego co najmniej jedna 

strzałka. Najczęściej występuje tylko raz, 

ale dla podniesienia czytelności schematu, 

moŜe być powtórzony. 

BLOK WEJŚCIA 

/ WYJŚCIA 

 

W tym bloku umieszcza się operacje 

wprowadzania (odczytu) danych 

albo wyprowadzania (zapisu) wyników. 

BLOK 

PRZETWARZANIA 

(WYKONAWCZY) 

 

W tym bloku umieszcza się obliczenia lub 

podstawienia. Blok moŜe zawierać grupę 

operacji, w efekcie których zmienia się war-

tość, postać lub miejsce zapisu danych. 

BLOK 

DECYZYJNY 

(WARUNKOWY, 

KIERUJĄCY) 

 

W tym bloku umieszcza się warunek logiczny 

(prosty lub złoŜony), decydujący o dalszej 

drodze postępowania. JeŜeli jest spełniony, 

wtedy są wykonywane operacje na „TAK”, w 

przeciwnym wypadku – na „NIE”. Do zapisu 

warunku logicznego naleŜy uŜywać symboli: 

=, 

, >, 

, <, 

 (i), 

 (lub). 

PROCES 

UPRZEDNIO 

ZDEFINIOWANY 

 

W tym bloku wpisuje się  nazwę procesu 

(podprogramu), który chcemy wykonać, zde-

finiowanego poza bieŜącym programem. 

PUNKT 

KONCENTRACJI 

 

Punkt koncentracji jest uŜywany dla podnie-

sienia czytelności schematu. Oznacza miej-

sce, do którego wchodzi kilka strzałek, 

i z którego wychodzi tylko jedna strzałka. 

 

2.5.

 

Schematy blokowe wybranych algorytmów 

W  rozdziale  tym  przedstawiono  schematy  blokowe  prostych  algorytmów. 

Szczegółową ich analizę pozostawiamy Czytelnikowi. 

 

 

NIE 

TAK 

 

background image

28 

 

Przykład 1. Wyznaczanie największej spośród trzech liczb:

.

,

,

c

b

a

 

 

 

Rys. 2.1. Schemat blokowy algorytmu wyznaczania największej spośród liczb: abc 

 

Przykład 2. Rozwiązywanie równania liniowego 

0

=

+

b

x

a

 

 

Rys. 2.2. Schemat blokowy algorytmu rozwiązywania równania liniowego 

STOP 

NIE 

TAK 

NIE 

TAK 

wczytaj  a, b 

 a=0  

b=0 

START 

 

b / a 

niesko

ń

czenie 

wiele rozwi

ą

za

ń

równanie 

sprzeczne 

wypisz  x 

STOP 

NIE 

TAK 

NIE 

NIE 

TAK 

TAK 

wczytaj  a, b, c 

a>b 

b>c 

a>c 

wypisz a 

wypisz c 

wypisz b 

wypisz c 

START 

background image

29 

 

Przykład 3. Wyszukiwanie największego elementu w tablicy n-elementowej 

A

 

 

Rys. 2.3. Schemat blokowy algorytmu wyznaczania największego elementu tablicy 

A

 

 

Przykład 4. Obliczanie silni liczby naturalnej n

 

 

Rys. 2.4. Schemat blokowy algorytmu obliczania silni liczby naturalnej n 

STOP 

wczytaj n 

START 

silnia 

 1 

 1 

NIE 

TAK 

i < n 

wypisz silnia 

 i + 1 

silnia 

 silnia*i 

TAK 

NIE 

wczytaj  n, 

A[1], …, A[n] 

START 

 

 2 

max 

 A[1] 

TAK 

NIE 

i > n 

wypisz max 

STOP 

max 

 A[i] 

 

 i + 1 

A[i] > max 

background image

30 

 

Przykład 5. Wyznaczanie średniej arytmetycznej n wczytywanych liczb. 
 

 

Rys. 2.5. Schemat blokowy algorytmu wyznaczania średniej arytmetycznej n liczb 

 
Przykład 6.
 Obliczanie NWD liczb x i y metodą z odejmowaniem. 
 

 

Rys. 2.6. Schemat blokowy algorytmu obliczania NWD metodą z odejmowaniem 

 n

 m

START 

 x,  m 

 y 

STOP 

NIE 

TAK 

NIE 

TAK 

wczytaj x, y 

 

n > m 

wypisz NWD(x,y)

 

n = m 

NWD(x,y) 

 n 

wczytaj  n 

START 

 

 0 

 n 

NIE 

k > 0 

wypisz  s 

STOP 

 s / n  

 s + a 

wczytaj  a 

 k 

 1 

TAK 

background image

31 

 

Przykład 7. Obliczanie NWD liczb x i metodą dzielenia z resztą. 

 

 

Rys. 2.7. Schemat blokowy algorytmu obliczania NWD metodą dzielenia z resztą 

 

Przykład 8. Szybkie (z minimalną liczbą mnoŜeń) obliczanie a

n

 

 

Rys. 2.8. Schemat blokowy algorytmu szybkiego obliczania a

n

 

wczytaj  a, n 

 n 

 

 a 

START 

wynik 

 1

 

NIE 

TAK 

 0 

wypisz wynik

 

STOP 

TAK 

NIE 

 k

 

wynik

wynik

 k / 2 

 x

mod 2 

 0 

NIE 

TAK 

wczytaj x, y 

 0 

 y 

 x mod y 

 r 

STOP 

START 

NWD(x,y) 

 x 

  wypisz NWD(x,y)

 

background image

32 

 

Przykład 9. Testowanie złoŜoności liczby naturalnej. 
 

 

Rys. 2.9. Schemat blokowy prostego algorytmu sprawdzającego, czy liczba jest złoŜona 

 
 
 
 

 

 

wczytaj  n 

START 

 n mod k

 

 2 

TAK 

NIE 

TAK 

NIE 

r = 0 

n – liczba zło

Ŝ

ona 

 k + 1 

STOP 

n = k

 

n – 

liczba pierwsza 

STOP 

background image

33 

 

Przykład 10. Rozwiązywanie równania kwadratowego 

.

0

2

=

+

+

c

x

b

x

a

 

 

 

 

Rys. 2.10. Schemat blokowy algorytmu rozwiązywania równania kwadratowego 

 
 
 

 

 

TAK 

NIE 

TAK 

D

 

=

 

0

x1 

 

b/2a 

x1

(

b+

√D

)/2a

 

STOP 

x2

(

b

√D

)/2a 

wypisz  x1, x2 

 b*b 

 4*a*c 

NIE 

 

START 

NIE 

TAK 

wczytaj  a, b, c 

 0

Równanie  

liniowe 

bx+c = 0 

D

 

 

0

Brak pierwiastków 

rzeczywistych 

x2 

 x1 

background image

34 

 

Przykład 11. PrzybliŜone obliczanie pierwiastka kwadratowego z liczby a 

 0. 

 

 

 

Rys. 2.11. Schemat blokowy algorytmu 

przybliŜonego obliczania pierwiastka kwadratowego z nieujemnej liczby a 

 

2.6.

 

Zadania

 

Narysować schematy blokowe algorytmów rozwiązujących problemy: 
 

Zad. 2.1. zamiany liczby naturalnej dziesiętnej na liczbę szesnastkową. 

Zad. 2.2. zamiany liczby szesnastkowej na liczbę w systemie dziesiętnym. 

Zad. 2.3. obliczania NWD tablicy liczb, tzn. 

).

,

,

,

(

NWD

2

1

n

a

a

a

K

 

Zad. 2.4. obliczania  najmniejszej  wspólnej  wielokrotności  pary  liczb  m  i  n  we-

dług zaleŜności: 

).

,

(

NWD

/

)

(

)

,

(

NWW

n

m

n

m

n

m

=

 

Zad. 2.5. skracania ułamka zwykłego. 

TAK 

wypisz x 

STOP 

NIE 

 a/4 

 

x1 

 x 

 0.5(x1+a/x1) 

NIE 

TAK 

wczytaj a 

 0 

eps 

 1e-9 

a  jest ujemne 

START 

|x-x1| < eps 

background image

35 

 

Zad. 2.6. dodawania  dwóch  ułamków  zwykłych  poprzez  sprowadzenie  ich  do 

wspólnego mianownika, a następnie skrócenie powstałego ułamka. 

Zad. 2.7. zamiany ułamka niewłaściwego na część całkowitą i ułamek właściwy. 

Zad. 2.8. zamiany ułamka dziesiętnego na ułamek w systemie binarnym. 

Zad. 2.9. zamiany ułamka okresowego na ułamek zwykły. 

Zad. 2.10. obliczania pierwiastka n-tego stopnia z liczby a, tzn. 

n

a

x

=

 metodą 

Newtona:  

1

1

)

1

(

+

+

=

n

i

n

i

i

x

n

x

n

a

x

, gdzie 

i

x

 

 kolejne przybliŜenie 

n

a

a

x

=

0

,   z dokładnością eps taką, Ŝe 

eps

x

x

i

i

<

+

1

Zad. 2.11. obliczania 

=

+

+

N

n

n

n

x

n

x

0

1

2

)!

1

2

(

)

1

(

sin

  oraz   

=

N

n

n

n

x

n

x

0

2

)!

2

(

)

1

(

cos

Argument x jest dowolną liczbą rzeczywistą, N 

 liczbą naturalną. 

Zad. 2.12. obliczania 

!

...

!

2

!

1

1

2

n

x

x

x

e

n

x

+

+

+

 dla podanego n i x.

 

Zad. 2.13. obliczania metodą Hornera wartości wielomianu 

.

)

(

0

1

1

1

a

x

a

x

a

x

a

x

w

n

n

n

n

n

+

+

+

+

=

K

  Na  przykład  dla 

:

4

=

n

 

))).

(

(

(

4

3

2

1

0

0

1

2

2

3

3

4

4

xa

a

x

a

x

a

x

a

a

x

a

x

a

x

a

x

a

+

+

+

+

=

+

+

+

+

 

 Dane wejściowe to:  n,   

n

a

a

a

,

,

,

1

0

K

 oraz konkretne 

.

0

x

x

=

 

Zad. 2.14. obliczania wartości pochodnej wielomianu 

)

(

x

w

n

 w punkcie 

.

0

x

x

=

 

Zad. 2.15. transponowania macierzy kwadratowej. 

Zad. 2.16. zamiany  parami  elementów  macierzy  kwadratowej,  połoŜonych  sy-

metrycznie względem drugiej (nie głównej) przekątnej. 

Zad. 2.17. obracania macierzy kwadratowej o 90, 180 i 270 stopni. 

Zad. 2.18. wyznaczania w tablicy liczby najbliŜszej zeru, ale róŜnej od zera. 

Zad. 2.19. sortowania „w miejscu” jednowymiarowej tablicy liczb. 

Zad. 2.20. sprawdzania, czy dwa wyrazy o tej samej długości są anagramami. 

Zad. 2.21. odwracania „w miejscu” łańcucha znakowego o długości n. Czy trze-

ba osobno rozwaŜać n parzyste i n nieparzyste? 

Zad. 2.22. dokładnego  dodawania  i  odejmowania  wielocyfrowych  (do  stu  cyfr) 

liczb naturalnych. Uwaga: liczby mogą posiadać róŜną ilość cyfr. 

 

 

background image

36 

 

3.

 

Podstawowe elementy języka Pascal 

 

3.1.

 

Zalety języka Pascal 

Pascal jest językiem wysokiego poziomu. Oznacza to, Ŝe uŜytkownik piszą-

cy  program  w  języku  Pascal  nie  musi  znać  szczegółów  wewnętrznej  budowy 
komputera.  Pascal  ma  wbudowane  precyzyjne  mechanizmy  kontroli  struktur  i 
reguł gramatycznych, dzięki temu łatwa jest identyfikacja i poprawianie błędów. 

Oprócz tego Pascal jest językiem: 

 

algorytmicznym 

 pomyślanym tak, aby łatwo moŜna w nim zapisywać 

algorytmy opracowane przez uŜytkownika, 

 

strukturalnym 

  poszczególne  fragmenty  algorytmu  moŜna  zapisać  w 

postaci wyraźnie wyodrębnionych struktur językowych, 

 

modularnym 

 program  moŜna budować z oddzielnych 'cegiełek' (mo-

dułów), wymienialnych bez naruszenia pozostałej części programu, 

 

publikacyjnym 

 istnieje moŜliwość  opublikowania oryginalnego,  inte-

resującego algorytmu w notacji języka Pascal. 

 

3.2.

 

Standardowe symbole i słowa kluczowe 

Języki programowania słuŜą do przedstawienia algorytmów w postaci, któ-

ra moŜe być wykonana przez komputer. Jedną z wygodnych form komunikacji 
człowieka  z  maszyną  jest  zapis  tekstowy,  dlatego  program  jest  tekstem

.  Ze 

względu  na  ograniczenia  praktyczne,  tekst  ten  jest  ciągiem  znaków  na  jednym 
poziomie, tzn. nie ma indeksowania stosowanego np. w matematyce czy fizyce. 

Program pascalowy buduje się z elementarnych jednostek tekstowych języ-

ka nazywanych symbolami. 

Symbol moŜe być pojedynczym znakiem bądź cią-

giem znaków alfabetu języka. 

skład alfabetu języka Pascal wchodzą następujące symbole: 

 

26 małych i 26 duŜych liter alfabetu łacińskiego oraz znak _, 

 

cyfry: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,  

 

znak odstępu (spacja),  

 

jednoznakowe symbole specjalne:  + 

 * / = ^ < > ( ) [ ] { }. , : ; ’ # $ @, 

 

dwuznakowe symbole specjalne: 


 

operator przypisania     := 



 

operatory relacji:  

<>   <=   >= 



 

oznaczenie zakresu   .. 

 

background image

37 

 

Słowem kluczowym nazywamy kaŜdy z wyrazów języka angielskiego: 

and,  array,  as,  asm,  begin,  case,  class,  const, constructor,  destructor,  
div,  do,  downto,  else,  end, except,  exports,  file,  finalization,  finally,  for, 
function,    goto,    if,    implementation,    in,    inherited,  initialization,    inline,  
interface,    is,    label,  library,    mod,    nil,    not,    object,    of,    or,    packed,  
procedure, program,  property,  raise,  record,  repeat, set, shl, shr, string,  
then,  threadvar,  to,  try,  type,  unit, until,  uses,  var,  while,  with,  xor. 

Słowa kluczowe są zastrzeŜone, tzn. programista nie moŜe przedefiniować 

ich znaczenia (uŜywając np. słowa kluczowego jako nazwy  zmiennej w swoim 
programie).  Słowa  kluczowe  mają  specjalne  znaczenie 

  są  nazwami  operato-

rów, elementami instrukcji itp. 

Identyfikatorem (nazwą) jest dowolnie długi ciąg liter, cyfr i znaków pod-

kreślenia, zaczynający się od litery lub znaku podkreślenia. Znaczące są jednak 
tylko 63 pierwsze znaki. Identyfikatory słuŜą do oznaczania programów, stałych,  
typów, zmiennych, etykiet, pól w rekordach, funkcji, procedur, parametrów for-
malnych  oraz  pól  i  metod  w  obiektach.  W  identyfikatorach  nie  wolno  uŜywać 
polskich liter z ogonkami. Oprócz tego: litery 

a

 i 

A

 są nierozróŜnialne, litery 

B

 są nierozróŜnialne itd. 

Przykłady poprawnych nazw:  

liczba_1, stala3, m, B_205, XXL.

 

Przykłady niepoprawnych nazw:  

2tablica, 

ś

cie

Ŝ

ka, Suma-1, #5.

 

Pewne  identyfikatory  mają  nazwy  ustalone  przez  twórców  Pascala.  UŜyt-

kownik moŜe przedefiniować pierwotne znaczenie tych identyfikatorów w sekcji 
deklaracji  programu  i  uŜywać  ich  w  zdefiniowanym  przez  siebie  znaczeniu. 
Oznacza to utratę wbudowanych w języku ułatwień. Predefiniowane identyfika-
tory słuŜą do oznaczania stałych, zmiennych, typów, funkcji i procedur. Zostały 
one  zgrupowane  w  modułach  standardowych 

System,  SysUtils  i  Math.  Aby 

uŜywać  tych  modułów,  naleŜy  je  zadeklarować  na  początku  programu,  poprze-
dzając ich nazwy słowem kluczowym 

uses. Nazwy z modułu System są dostęp-

ne bez potrzeby deklaracji tego modułu. 

 

3.3.

 

Sposoby zapisu liczb 

Liczby w języku Pascal zapisuje się w sposób podobny do notacji matema-

tycznej.  W  przypadku  liczb  rzeczywistych,  zamiast  przecinka  oddzielającego 
część całkowitą od części ułamkowej, stosuje się kropkę. 

Liczby  całkowite  w  systemie  dziesiętnym  zapisuje  się  uŜywając  cyfr  ze 

zbioru  {0,  1,  …,  9}  poprzedzonych  znakiem  +  lub 

.  Znak  +  moŜna  pominąć. 

Liczba  moŜe  zaczynać  się  od  jednej  lub  więcej  cyfr  0.  Między  pierwszą  cyfrą 

background image

38 

 

liczby, a znakiem + lub 

 moŜe być odstęp. Przykłady poprawnych liczb w sys-

temie dziesiętnym: 2013, +02013, + 92, 

 0008885. 

Liczby całkowite w systemie szesnastkowym tworzy się z cyfr naleŜących 

do  zbioru  {0,  1,  2,  …,  9,  A,  B,  C,  D,  E,  F},  poprzedzonych  symbolem  $  oraz 
ewentualnym znakiem + lub 

. Symbol $ musi występować bezpośrednio przed 

pierwszą  cyfrą.  Przykłady  poprawnych  liczb,  zapisanych  w  systemie  szesnast-
kowym:  $9802, +$8FA3C, 

$3FFFFFF, 

$0007FFFFFF. 

Liczba rzeczywista zbudowana jest z mantysy, litery E (lub e) oraz cechy

Mantysa jest liczbą dziesiętną, składającą się z części całkowitej, kropki i części 
ułamkowej, i moŜe być poprzedzona znakiem + lub 

. Cecha jest liczbą całko-

witą poprzedzoną ewentualnie znakiem + lub 

. Mantysa i cecha moŜe zawierać 

wyłącznie cyfry dziesiętne. Dozwolone jest opuszczenie części ułamkowej wraz 
z poprzedzającą ją kropką albo litery E i cechy. Symbolicznie liczbę rzeczywistą 
moŜna zapisać tak: mantysaEcecha, czemu odpowiada wartość  mantysa

10

cecha

Przykłady: 

56.980, 

4.6592e

103, 0.314E+1, 2013E+00, +89.9377, 686.2e23. 

 

3.4.

 

Sposoby notowania komentarzy 

Komentarzem nazywamy dowolny ciąg znaków, ograniczony z lewej stro-

ny nawiasem klamrowym otwierającym {, a z prawej strony nawiasem klamro-
wym  zamykającym  }.  Taki  komentarz  moŜe  rozciągać  się  na  wiele  linii  tekstu 
programu.  Nawias  {  nie  musi  być  pierwszym  znakiem  w  danej  linii.  Znakami 
ograniczającymi  komentarz  mogą  być  takŜe  symbole  podwójne  (*  i  *).  JeŜeli 
bezpośrednio po znaku { lub (* występuje symbol $, to taki komentarz jest trak-
towany jak dyrektywa kompilatora. Komentarze moŜna zagnieŜdŜać. Tekst roz-
poczynający się od znaków // jest traktowany jak komentarz, ale tylko do końca 
bieŜącej linii. Umieszczanie komentarzy w programie naleŜy do dobrej praktyki 
programistycznej. Przykłady komentarzy: 

{ Obliczanie miejsc zerowych funkcji kwadratowej } 
(* Zapisanie wyników do pliku *) 
// Sprawdzenie, czy delta jest większa od zera. 
{ Komentarz (* zagnieŜdŜony *) }  albo  { Komentarz { zagnieŜdŜony } } 
{$mode objfpc} 

 dyrektywa kompilatora. 

Separatorami są w programie pascalowym: średnik, przecinek, komentarz, 

odstęp  (spacja),  przejście  do  następnego  wiersza  (enter)  oraz  wszystkie  znaki 
sterujące ASCII, tzn. znaki o kodach od 0 do 31. Separatory słuŜą do rozdziela-
nia poszczególnych elementów programu. Dowolne dwa słowa kluczowe, iden-
tyfikatory oraz liczby muszą być rozdzielone co najmniej jednym separatorem. 

background image

39 

 

3.5.

 

Zasady deklarowania stałych i zmiennych 

Stałe,  zwane teŜ  literałami,  mają  za  zadanie  zwiększenie  czytelności  kodu 

ź

ródłowego. KaŜde wystąpienie identyfikatora stałej jest równowaŜne wystąpie-

niu  przypisanego  mu  wyraŜenia  stałego.  WyraŜenie  stałe  to  takie,  które  moŜe 
być  obliczone  przez  kompilator  bez  wykonywania  programu.  Stałe  nie  muszą 
być  liczbami.  Wartości  stałych  nie  zmieniają  się  przez  cały  czas  działania  pro-
gramu.  Predefiniowane  w  module 

System  stałe  to:  False,  True,  MaxInt

MaxLongint. Stałe definiujemy po słowie const

Przykłady definicji stałych: 

const 

kierunek = 'informatyka'; 
liczba_pi = 3.1415926; 
c = 299792458; 
falsz = false; 
sygnal = #$7; // znak o kodzie ASCII równym $7 

WyraŜenie stałe jest to wyraŜenie, które moŜe być obliczone przez kompi-

lator. W wyraŜeniach stałych dopuszczalne jest stosowanie następujących funk-
cji  standardowych: 

Abs,  Addr,  Chr,  Hi,  High,  Length,  Lo,  Low,  Odd,  Ord

Pred,  Round,  SizeOf,  Succ,  Swap,  Trunc.  WyraŜenia  liczbowe  stałe  są  typu 
Longint albo Extended, w zaleŜności od kontekstu. MoŜna teŜ tworzyć wyraŜe-
nia  stałe  typu  łańcuchowego  lub  zbiorowego.  Za  pomocą  słowa  kluczowego 
const wyraŜeniu stałemu moŜna nadać nazwę i stosować tę nazwę w programie, 
zamiast wyraŜenia. 

Przykłady wyraŜeń stałych: 

const cena = '100' + Chr(36); 
      rozmiar = (1 shl 20); 

Zmienną  nazywamy  daną,  która  moŜe  przyjmować  róŜne  wartości  w  ra-

mach  typu  przypisanego  do  tej  zmiennej.  Zmienna  jest  wielkością  zmieniającą 
swoją  wartość  w  trakcie  wykonywania  programu.  Zmienna  powinna  być  zade-
klarowana  przed  pierwszym  uŜyciem.  JeŜeli  zmienna  ma  w  programie  zasięg 
globalny, to w  momencie deklaracji jest jej przypisywana wartość zerowa. De-
klaracje zmiennych sygnalizują kompilatorowi konieczność zarezerwowania dla 
nich odpowiedniej ilości pamięci. Deklaracja zmiennej ma postać: 
 
var   nazwa_zmiennej : typ_zmiennej
 
gdzie: 

 

var 

 słowo kluczowe oznaczające deklarację zmiennej, 

 

nazwa_zmiennej 

 identyfikator zmiennej, 

 

typ_zmiennej 

 zbiór wartości, jaki moŜe przyjmować ta zmienna. 

background image

40 

 

Zmienna przechowuje pewną informację o stanie maszyny cyfrowej w jed-

nej  lub  więcej  komórkach  pamięci  operacyjnej  (rys.  3.1).  Nazwa  zmiennej  po-
winna sugerować rolę tej zmiennej w programie. 

 

 

Rys. 3.1. Graficzna reprezentacja zmiennej 

 
Przykłady deklaracji zmiennych: 

var miesiac : Byte
    pole, obwod : Double
    znak : Char
    tytul, autor : string

Podczas deklaracji, zmiennym moŜna nadać wartości początkowe:

 

var miesiac : Byte = 5; 
    pole : Double = 100.0; 
    znak : Char = 'Z'; 
    tytul : string = 'Pan Tadeusz'; 

Uwaga:  deklaracja  

pole,  obwod  :  Double  = 100.0;

 

jest niepoprawna, 

poniewaŜ niewiadomo, której zmiennej naleŜy przypisać wartość 100.0. 

 

3.6.

 

Definicja typu i rodzaje typów danych 

Typem nazywamy zbiór wartości zmiennej. Typy dzieli się na standardowe 

(wbudowane)  i  niestandardowe.  Typy  standardowe  są  predefiniowane  przez 
twórców języka  programowania,  natomiast  wszystkie  typy  wprowadzone  przez 
programistę muszą zostać przez niego opisane. Stosowanie w programie identy-
fikatorów  typów  zwiększa  czytelność  kodu  źródłowego.  KaŜda  stała,  zmienna, 
wyraŜenie lub wartość zwracana przez funkcję jest pewnego typu. Typ wyraŜe-
nia określa takŜe zestaw dopuszczalnych operacji, które moŜna na nim przepro-
wadzić. Definicja typu jest następująca:    

type  nazwa_typu = opis_typu

 

type 

 słowo kluczowe oznaczające definicję typu, 

 

nazwa_typu 

 identyfikator typu, 

 

opis_typu 

 opis jednego z typów: wyliczeniowego, okrojonego, łańcu-

chowego, tablicowego, rekordowego, zbiorowego, plikowego, klasowe-
go,  wskaźnikowego,  proceduralnego  albo  identyfikator  typu  standardo-
wego lub zdefiniowanego wcześniej przez uŜytkownika. 

Przykłady definicji typów: 

identyfikator 

delta 

123 

(byte) 

zmienna 

typ 

warto

ść

 

background image

41 

 

type pora_roku = (Wiosna, Lato, Jesien, Zima); 
     liczba_rzymska = string
     temperatura = Real
     macierz = array [1..5, 1..5] of Integer

 

Typy predefiniowane dzieli się, ze względu na postacie i zakresy, na: 

 

podstawowe 

 takie same bez względu na implementację języka, 

 

ogólne 

 zaleŜne od procesora i systemu operacyjnego. 

Do typów podstawowych zalicza się typy: proste, łańcuchowe, struktural-

ne, typy opisujące obiekty, wskaźnikowe, proceduralne i wariantowe. 

Typy proste dzieli się na: 

 

porządkowe:  wyliczeniowe, całkowite, znakowe, logiczne, okrojone, 

 

rzeczywiste:  

RealSingleDoubleExtendedCompCurrency

 

Wszystkie  typy  proste  składają  się  ze  skończonego  i  uporządkowanego 

zbioru wartości. Z kaŜdą wartością typu porządkowego jest związana liczba po-
rządkowa (liczba całkowita) jednoznacznie określająca umiejscowienie tej war-
tości wśród wszystkich wartości danego typu. 

Na  podstawie  typów  prostych  moŜna  tworzyć  inne,  bardziej  rozbudowane 

struktury danych, tzw. 

typy strukturalne. Wprowadzono je w związku z dąŜe-

niem do moŜliwie szerokiego zakresu zastosowań języka Pascal. 

Typami struk-

turalnymi są typy: tablicowe, rekordowe, zbiorowe i plikowe. 
 

Typ wyliczeniowy stosuje się do zbiorów o niewielkiej liczbie elementów, 

na których nie wykonuje się operacji arytmetycznych. 

type  identyfikator_typu = (lista_identyfikatorów);  

W nawiasach ( ) podaje się listę róŜnych nazw, które tworzą zbiór wartości 

tego typu, przy czym pierwszej nazwie odpowiada wartość 0, drugi 

 1, trzeciej 

 2 itd. Przykłady definicji typu wyliczeniowego: 

type  pory_dnia = (rano, poludnie, wieczor, noc); 
     moje_grupy = (L5, L7, L12, L13, L16, L18); 
         kolory = (czerwony, zielony, niebieski); 
        greckie = (alfa, beta, gamma, delta); 

 

Wszystkie 

typy całkowite są predefiniowane i są podzbiorami zbioru liczb 

całkowitych  (który  w  matematyce  jest  nieskończenie  liczny).  Wartość  porząd-
kowa elementu dowolnego typu całkowitego jest równa wartości tego elementu. 
Typami porządkowymi nie są jednak typy całkowite 

QWord i Int64. Typy cał-

background image

42 

 

kowite  języka  Free  Pascal  zebrano  w  tabeli  3.1.  Dla  typów 

Integer  i  Cardinal 

podano liczbę bajtów dla 32 bitowej implementacji języka. 

 
Tabela 3.1. Typy całkowite i ich zakresy 

Typ 

 

wartość minimalna 

wartość maksymalna 

Shortint 

128 

127 

Smallint 

32 768 

32 767 

Longint 

2 147 483 648 

2 147 483 647 

Byte 

255 

Word 

65 535 

DWord, 
LongWord
 

4 294 967 295 

QWord 

18 446 744 073 709 551 615 

Int64 

9 223 372 036 854 775 808 

9 223 372 036 854 775 807 

Integer 

2 147 483 648 

2 147 483 647 

Cardinal 

4 294 967 295 

P

 – typ podstawowy, 

O

 – typ ogólny (zaleŜny od rodzaju procesora i sys. oper.),  

B – liczba bajtów pamięci potrzebna do przechowywania zmiennej tego typu 

Przykłady deklaracji zmiennych typu całkowitego: 

var  a, b, c : Shortint
      silnia : QWord
     licznik : Cardinal

W  programach  zaleca  się  stosowanie  ogólnych  typów  całkowitych,  które 

lepiej wykorzystują rodzaj procesora i zainstalowany system operacyjny. 

 

W języku  Free  Pascal  zdefiniowano  dwa  podstawowe 

typy  znakowe  i je-

den ogólny typ znakowy (tab. 3.2). 

 

Tabela 3.2. Typy znakowe 

Typ 

 

Zbiór wartości 

Liczba znaków 

AnsiChar 

znaki uporządkowane zgodnie z tabelą roz-

szerzonego zestawu znaków ASCII (0

÷

255) 

256 

WideChar 

znaki uporządkowane zgodnie z tabelą zna-

ków standardu UNICODE (0

÷

65535) 

65536 

Char 

znaki uporządkowane zgodnie z tabelą roz-

szerzonego zestawu znaków ASCII (0

÷

255) 

256 

P

 – typ podstawowy, 

O

 – typ ogólny (zaleŜny od rodzaju procesora i sys. oper.),  

B – liczba bajtów pamięci potrzebna do przechowywania zmiennej tego typu 

background image

43 

 

Przykłady deklaracji zmiennych typu znakowego: 

var    znak : Char
     litera : AnsiChar
      calka : WideChar

 

Podstawowym 

typem logicznym jest typ Boolean. Pozostałe typy logiczne, 

wymienione w tabeli 3.3, stosuje się dla zapewnienia zgodności programów pas-
calowych z programami napisanymi w innych językach. Wartości typów logicz-
nych są oznaczane za pomocą dwóch predefiniowanych stałych 

False i True

 

Tabela 3.3. Typy logiczne 

Typ 

 

zbiór wartości (wartość porządkowa) 

Boolean 

False (0) 

True (1) 

ByteBool 

False (0) 

True (od 1 do 255) 

WordBool 

False (0) 

True (od 1 do 65535) 

LongBool 

False (0) 

True (od 1 do 4 294 967 295) 

P

 – typ podstawowy, 

O

 – typ ogólny (zaleŜny od rodzaju procesora i sys. oper.),  

B – liczba bajtów pamięci potrzebna do przechowywania zmiennej tego typu 
 
Przykłady deklaracji zmiennych typu logicznego: 

var     decyzja : Boolean
             ok : Boolean
    czy_trojkat : Boolean

 

Typy okrojone słuŜą do ograniczenia zakresów wartości dowolnego z do-

tychczas opisanych typów porządkowych. Definicja typu okrojonego ma postać: 
 
type  identyfikator_typu = stala1 .. stala2; 
 
Obie stałe muszą być tego samego typu porządkowego i stala

 stala2. 

 
Przykłady definicji typów okrojonych: 

type  litera   = 'a' .. 'z'; 
type  cyfra_dz = 0 .. 9; 
type  cyfra_zn = '0' .. '9'; 
type  miesiac  = 1 .. 12; 
 
type klasy = (Ia, Ib, IIa, IIb, IIc, IId, IIIa, IIIb); 
     klasy_drugie = IIa .. IId; 

 

 

background image

44 

 

Do  typów  prostych  naleŜą  równieŜ  standardowe 

typy  rzeczywiste  (tab. 

3.4), które nie są porządkowymi. KaŜdy z dostępnych typów rzeczywistych jest 
dyskretnym  i  skończonym  podzbiorem  zbioru  liczb  rzeczywistych.  Z  tego  po-
wodu nie wszystkie liczby rzeczywiste (z osi liczbowej) posiadają dokładną re-
prezentację w typach rzeczywistych. Maksymalna liczba cyfr znaczących  man-
tysy wynosi 7, 15 albo 19 w sytuacji, gdy wyspecyfikowano kropkę dziesiętną. 
Najmniejsza wartość liczb typu 

RealSingleDoubleExtended jest, w przybli-

Ŝ

eniu, równa największej wartości dodatniej pomnoŜonej przez (

1). Typ 

Comp 

stosuje  się  do  zapamiętywania  duŜych  liczb  całkowitych.  Kompilator  traktuje 
dane tego typu jako wartości rzeczywiste bez części wykładniczej. Typ 

Curren-

cy słuŜy do obliczeń pienięŜnych, gdyŜ liczby tego typu mają postać dokładną. 
 

Tabela 3.4. Typy rzeczywiste 

Typ 

 

Najmniejsza 

wartość 

dodatnia 

Największa 

wartość 

dodatnia 

Liczba 

cyfr zna-

czących  

mantysy 

Real 

albo

 

zaleŜy 

od 

 platformy 

zaleŜy 

od 

 platformy

 

zaleŜy 

od 

platformy 

Single 

2

-149

1.40E

45 

3.4E+38 

7 albo 8 

Double 

2

-1074

4.94E

324 

1.7E+308 

15 albo 16 

Extended 

10 

2

-16445

3.65E

4951 

1.1E+4932 

19 albo 20 

 

 

 

wartość najmniejsza 

wartość największa 

 

Comp 

2

63

 =

 

9223372036854775808

 

2

63

 

 1 =

 

9223372036854775807

 

19 albo 20 

Currency 

922337203685477.5808

 

922337203685477.5807

 

19 

P

 – typ podstawowy, 

O

 – typ ogólny (zaleŜny od rodzaju procesora i sys. oper.),  

B – liczba bajtów pamięci potrzebna do przechowywania zmiennej tego typu 

 

Przykłady deklaracji zmiennych typu rzeczywistego: 

var  a, b, c : Single
        suma : Double
     moje_pi : Extended
      silnia : Comp
       kwota : Currency

 

 

background image

45 

 

Zmienne 

typu  łańcuchowego  słuŜą  do  przechowywania  ciągów  znaków, 

głównie typu 

Char. Free Pascal pozwala definiować łańcuchy krótkie (takie jak 

w  Turbo  Pascalu)  oraz  długie  (tab.  3.5).  Pamięć,  dla  łańcuchów  krótkich,  jest 
przydzielana  statycznie.  Mogą  one  jednak  zmieniać  swoją  długość  (w  trakcie 
wykonywania programu) od 0 do maksymalnej długości N, podanej w definicji 
typu krótkiego lub do wartości domyślnej równej 255. 

KaŜdy znak łańcucha krótkiego zajmuje 1 bajt pamięci. Pierwszy znak 

znajduje się pod indeksem 1, drugi – pod indeksem 2 itd. Po indeksem 0 jest 
przechowywana informacja o aktualnej długo
ści łańcucha. 

Zmienna typu łańcucha długiego zajmuje w pamięci 4 bajty i jest wskaźni-

kiem  do  miejsca  w  pamięci,  w  którym  znajduje  się  łańcuch.  Długość  łańcucha 
nie jest bezpośrednio pamiętana, tzn. jest wyznaczana na Ŝądanie uŜytkownika. 

 

Tabela 3.5. Typy łańcuchowe 

Typ 

Długość 

Uwagi 

string[

N

2

N

255

 

Zawsze oznacza łańcuch krótki. 

string 

zaleŜy od 

{

$H}  

Typ

 string oznacza typ ShortString w stanie {$H

}. 

Typ

 string oznacza typ AnsiString w stanie {$H+}. 

ShortString 

255 

Zawsze oznacza łańcuch krótki. 

AnsiString 

dowolna 

Długość ograniczona tylko rozmiarem pamięci. Pamięć 

przydzielana dynamicznie na stercie. Przechowuje znaki 

typu 

Char. Łańcuch ten jest zakończony znakiem pu-

stym (#0), którego jednak nie zalicza się do łańcucha. 

WideString 

dowolna 

Ma te sami właściwości, co typ 

AnsiString

z tym, Ŝe przechowuje znaki typu 

WideChar

Standardowo dyrektywa {

$H} kompilatora jest włączona, tzn. ma stan {$H+}. 

 

Elementami  łańcucha  są  wartości  typu 

Char albo  WideChar.  Do  poszcze-

gólnych elementów łańcucha moŜna się odnosić tak, jak do elementów tablicy 

 

poprzez indeks, np. s[1], s[2], s[10]. Typ łańcuchowy jest jedynym typem tabli-
cowym,  na  którym  dopuszczalne  są  operacje  agregujące  (np.  '+')  oraz  wartości 
tego typu mogą być parametrami procedur 

Write, Writeln, Read i Readln

Przykłady deklaracji zmiennych typu łańcuchowego: 

var    imie : string[16]; 
     s1, s2 : string
      linia : ShortString
    wydzial : AnsiString

 

 

background image

46 

 

Typ  tablicowy jest  typem  strukturalnym,  czyli  typem  złoŜonym.  Zmienna 

typu tablicowego przechowuje elementy tego samego typu (prostego, łańcucho-
wego lub strukturalnego). Ogólna definicja typu tablicowego ma postać: 

 

type identyfikator_typu = array [typy_indeksoweof typ_skladowy
 
Typy indeksowe są opisami typu porządkowego z wyjątkiem 

Longint. Typ skła-

dowy oznacza dowolny typ. Przykłady definicji typów tablicowych: 

{tablice 1-wymiarowe}

 

type  IntList = array [1..100]   of  Integer;  
type CharCode = array ['A'..'Z'] of  Byte;  
var  A : IntList;    
var  C : CharCode; 
… 
A[10] := 14000;  C['Z'] := 255; // przykłady u

Ŝ

ycia 

{tablice 2-wymiarowe} 

type  Matrix  = array [0..10, 

5..5] of Real;    

      Macierz = array [0..10] of array [

5..5] of Real;  

  
{tablica 2-wymiarowa jako tablica tablic 1-wymiarowych} 
type   Tab = array [1..100] of Single; 
     Wynik = array [1.. 50] of Tab; 

 

 
var   M : Matrix;    
var   W : Wynik; {tablica 2-wymiarowa} 
{przykłady odwoła

ń

 do elementów tablicy 2-wymiarowej} 

M[1,0]  := 

1.043;  // albo  M[1][0]  := 

1.043; 

W[2,35] := 21.25;   // albo  W[2][35] := 21.25; 

 

Odwoływanie się do poszczególnych elementów tablicy wymaga wprowa-

dzenia pojęcia selektora. W typie tablicowym, podobnie jak w łańcuchowym, se-
lektorem są nawiasy kwadratowe, w których podajemy indeks(y) elementu(ów), 
do którego(ych) odwołujemy się. 

Lazarus FPC jest dopuszczalna 

definicja zmiennych typu tablicowego 

z wartościami początkowymi

Przykłady takich definicji: 

type  Tabz  = array [0..5]  of  Char
var   znaki : Tabz = ('a', 'z', 'A', 'X', '3', '#'); 
 
type  Dane   = array [1..4] of  Smallint
var   liczby : Dane = (1, 2, 100, 5); 

 

background image

47 

 

Dotychczas  rozwaŜane  tablice  były  statyczne,  tzn.  ich  rozmiar  był  znany 

kompilatorowi przed kompilacją. MoŜliwe jest takŜe  definiowanie tablic o roz-
miarze określanym w trakcie wykonywania programu. Typ indeksowy takich ta-
blic  jest  domyślnie  całkowity. 

Tablice  dynamiczne,  domyślnie,  indeksuje  się 

od 0. Tablice dynamiczne definiuje się następująco: 
 
type
  identyfikator_typu = array  of  typ_skladowy
 
Przykład definicji tablicy dynamicznej: 
 

type  tablica = array of Integer

var  A : tablica; 

BEGIN 
  SetLength(A, 10);  { High(A) 

 9 } 

  A[0] :=  15; 

  A[9] := 

24; 

END. 

 
Rekord jest złoŜoną strukturą danych, której składowe, zwane polami, mo-

gą być róŜnych typów. Pola rekordu równieŜ mogą być typu strukturalnego. Typ 
rekordowy definiuje się następująco: 

 
type identyfikator_typu = record  
                                             pole1 : typ1; 
                                             pole2 : typ2; 
                                             … 
                                             poleN : typN; 
                                          

end

 
Przykład definicji typu rekordowego:  

type  TOsoba = record 
       imie  : string[20]; 
       plec  : Char
       wiek  : Byte
       adres : array [1..10] of string
      end;  

 

Cechy  typu rekordowego: 

 

rekordy mogą być zagnieŜdŜane, tzn. pole rekordu moŜe być ponownie 
typu rekordowego (struktura hierarchiczna), 

 

pola tego samego typu mogą wystąpić jako oddzielone przecinkiem, 

background image

48 

 

 

dostęp  do  poszczególnych  pól  zmiennej  rekordowej  odbywa  się  za  po-
mocą kropki oraz nazwy pola (np. zespolona.rezespolona.im), 

 

podobnie  jak  w  przypadku  tablic,  zmienna  rekordowa,  jako  całość,  nie 
moŜe być parametrem procedur: 

WriteWritelnReadReadln.  

 

zmienne rekordowe mogą wystąpić w instrukcji przypisania pod warun-
kiem, Ŝe są tego samego typu, 

 

pole rekordu moŜe występować we wszystkich operacjach, jakie są do-
puszczalne dla typu, jakiego jest to pole; w szczególności pole rekordu 
moŜe być parametrem procedury lub funkcji. 

 
Typ zbiorowy (mnogościowy) jest zbiorem wszystkich podzbiorów danego 

typu porządkowego. Typ zbiorowy definiuje się następująco: 
 
type  identyfikator_typu = set of  typ_porzadkowy
 

Liczba elementów typu porządkowego, zwanego teŜ bazowym, występują-

cego w definicji typu zbiorowego, nie moŜe przekraczać 256. Ponadto liczby po-
rządkowe  tych  elementów  muszą  naleŜeć  do  przedziału  [0,  255]  (dlatego  nie-
prawidłowa jest definicja: 

set of [

5..5]). Wartości typu zbiorowego zapisuje się 

przez podanie w nawiasach kwadratowych, w dowolnej kolejności, listy elemen-
tów danego zbioru. Zapis [ ] oznacza zbiór pusty. 

 
Przykład 1. Definicja kilku typów zbiorowych. 

type 
    CharSet = set of Char
    Digits  = set of 0..9; 
    Day     = (Sun, Mon, Tue, Wed, Thu, Fri, Sat); 
    Days    = set of Day; 

 

Przykład 2. Deklaracja i uŜycie zmiennych typu zbiorowego. 

type  znaki = set of Char
var   a, b, c : znaki; 
            z : Char
BEGIN 
  a := ['0'..'9',  'a'..'z',  'A'..'Z']; 
  b := [ ]; // zbiór pusty 
  z := #27; // znak ASCII o kodzie równym 27 
  c := [z]; 
END. 

 

 

background image

49 

 

Kompilator dopuszcza zmianę wartości wyraŜenia jednego typu na wartość 

innego typu. Operacja taka nazywa się 

konwersją typu. Zmiany typu dokonuje 

się  za  pomocą  konstrukcji:  identyfikator_typu  (wyraŜenie).  Identyfikator  typu 
i wyraŜenie muszą być równocześnie typu porządkowego albo wskaźnikowego. 
W  wyniku  konwersji  między  dwoma  niezgodnymi  typami  porządkowymi  albo 
dwoma niezgodnymi typami wskaźnikowymi moŜe nastąpić obcięcie lub rozsze-
rzenie wartości przekształcanego wyraŜenia. 

 

Przykłady jawnych konwersji: 

Integer('5') + Integer('6') 

 53 + 54 = 107 

 
var   b  : Byte
     log : Boolean
    znak : Char
       c : Integer
… 
if  not log and Boolean(b) then  b := Byte('a'); 
znak := Char(65); 
c := Byte('z') + 5; 

 
Przykład niejawnej konwersji: 

var    c : Integer
    x, d : Double
… 
x := c + d; 

 
W instrukcji  

x := c + d;

 zachodzi niejawna konwersja zmiennej 

c

 typu cał-

kowitego na zmienną typu rzeczywistego, poniewaŜ 

d

 jest typu rzeczywistego. 

 

3.7.

 

Rodzaje operatorów

 

Aby zdefiniować w programie wyraŜenie arytmetyczne, logiczne itp., obli-

czające pewną wartość, naleŜy uŜyć sensownej kombinacji operandów i operato-
rów.  Operandami  (argumentami)  są  zmienne,  stałe  lub  inne  wyraŜenia.  Zbiór 
operatorów jest natomiast ściśle określony. Operatory dzieli się m. in. na: aryt-
metyczne, logiczne, relacyjne, teoriomnogościowe i konkatenacji. 

 
Operatory  arytmetyczne  słuŜą  do  obliczania  wartości  wyraŜeń  liczbo-

wych. Typ zwracanego wyniku zaleŜy od typu argumentów (tab. 3.6). Nie istnie-
ją operatory potęgowania ani pierwiastkowania. Operacje te moŜna zrealizować 
poprzez wielokrotne mnoŜenie albo za pomocą funkcji standardowych 

Exp i Ln

 

background image

50 

 

Tabela 3.6. Operatory arytmetyczne 

Operator   

Nazwa operacji 

Typ argumentów 

Typ wyniku 

identyczność 

całkowity 

całkowity 

rzeczywisty 

Extended 

−−−−

 

zmiana znaku 

całkowity 

całkowity 

rzeczywisty 

Extended 

dodawanie 

całkowity 

całkowity 

rzeczywisty 

rzeczywisty 

 

odejmowanie 

całkowity 

całkowity 

rzeczywisty 

rzeczywisty 

mnoŜenie 

całkowity 

całkowity 

rzeczywisty 

rzeczywisty 

dzielenie 

całkowity 

Extended 

rzeczywisty 

Extended 

div 

dzielenie całkowite 

całkowity 

całkowity 

mod 

reszta z dzielenia 

całkowity 

całkowity 

J

 – operator jednoargumentowy,  

D

 – operator dwuargumentowy 

 

Dzielenie całkowite argumentu a przez argument b, czyli a 

div b, zwraca: 

 

największą liczbę całkowitą mniejszą lub równą ilorazowi a/b, gdy a/b 

 0, 

 

najmniejszą liczbę całkowitą większą lub równą ilorazowi a/b, gdy a/b < 0, 

 

błąd, gdy b = 0. 

 
15 

div 6 

 2,    

15 

div 6  

  

2,      15 

div 

6  

  

2,   

15 

div 

6  

  2. 

 

Reszta z dzielenia argumentu a przez argument b, czyli a 

mod b, jest okre-

ś

lona następująco: a 

 (a 

div b)*b. Znak wyniku jest taki, jak znak argumentu a

 
15 

mod 6 

 3,   

15 

mod 6  

  

3,   15 

mod 

6  

  3,   

15 

mod 

6  

  

3. 

 

JeŜeli oba argumenty operacji +, 

, *, /, 

divmod są róŜnych typów całko-

witych, to przed wykonaniem działania są one przekształcane to typu wspólne-
go,  który jest standardowym typem całkowitym,  mieszczącym w sobie wszyst-
kie moŜliwe wartości typów obu argumentów. Wynik teŜ jest tego typu. 

 

Operatory  logiczne  słuŜą do  wykonywania  operacji  logicznych  na  warto-

ś

ciach typów logicznych oraz na wartościach całkowitych (tab. 3.7). JeŜeli oby-

dwa  argumenty  operatorów 

and,  or  lub  xor  są  róŜnych  typów  całkowitych,  to 

typem wyniku jest typ wspólny obu argumentów. 

background image

51 

 

Operacja  a 

shl  b  (a  shr  b)  przesuwa  bity  liczby  a  o  b  bitów  w  lewo 

(w prawo). Typ wyniku jest taki sam, jak typ operandu a

Podczas  przesuwania  bitów  w  lewo,  z  prawej  strony  liczby  pojawiają  się 

dodatkowe  bity  równe  0.  Bity  stojące  na  najbardziej  znaczących  pozycjach  są 
tracone. Podobnie jest w przypadku przesuwania bitów w prawo. NaleŜy pamię-
tać, Ŝe podczas przesuwania bitów w prawo, bit znaku nie ulega powieleniu. 

Operację 

shl  moŜna  stosować  do  szybkiego  mnoŜenia  liczby  całkowitej 

przez potęgę liczby 2, mianowicie: a 

shl b 

 a

2

b

, np. 7 

shl 5 

 7

2

= 224. 

Operację 

shr  moŜna  stosować  do  szybkiego  dzielenia  całkowitego  przez 

potęgę liczby 2, mianowicie: a 

shr b 

 a div 2

b

, np. 37 

shr 3 

 37 div 2

= 4. 

 

Tabela 3.7. Operatory logiczne 

Operator   

Nazwa operacji 

Typ argumentów 

Typ wyniku 

not 

negacja 

całkowity 

całkowity 

logiczny 

Boolean 

and 

koniunkcja 

(logiczne i

całkowity 

całkowity 

logiczny 

Boolean 

or 

alternatywa 

(logiczne lub

całkowity 

całkowity 

logiczny 

Boolean 

xor 

róŜnica symetryczna, 

alternatywa wyklu-

czająca (e

xclusive 

or), (logiczne albo)  

całkowity 

całkowity 

logiczny 

Boolean 

shl 

przesunięcie bitowe 

w lewo (

shift left

całkowity 

całkowity 

shr 

przesunięcie bitowe 

w prawo (

shift right

całkowity 

całkowity 

J

 – operator jednoargumentowy,  

D

 – operator dwuargumentowy 

 

JeŜeli  operandy  operatora  logicznego  są  typów  logicznych,  to  wynik  jest 

określony zgodnie z tabelą 3.8. 

 

Tabela 3.8. Wyniki operacji logicznych na argumentach typów logicznych 

not a 

not b 

a 

and b 

a 

or b 

a 

xor 

False 

False 

True 

True 

False 

False 

False 

False 

True 

True 

False 

False 

True 

True 

True 

False 

False 

True 

False 

True 

True 

True 

True 

False 

False 

True 

True 

False 

background image

52 

 

JeŜeli  operandy  operatora  logicznego  są  typów  całkowitych,  to  wynik  jest 

całkowity. Operacje logiczne wykonywane są na wszystkich parach bitów stoją-
cych na jednakowych pozycjach w obu operandach, zgodnie z tabelą 3.9. 

 

Tabela 3.9. Wyniki operacji logicznych na parze bitów p i q liczb całkowitych 

not p 

not q 

p 

and q 

p 

or q 

p 

xor 

0 

0 

1 

0 

 
Przykłady uŜycia operatorów logicznych: 

(a > 2) and (a < 9) 

 // 

dobrze,  

a > 2  and  a < 9

  // 

ź

le 

(rok mod 4 = 0) and (rok mod 100 <> 0) 
                 or (rok mod 400 = 0)

 

not a in [1..5]

 

 
Operatory  relacyjne słuŜą  do  konstrukcji  wyraŜeń  porównania.  Są  to  na-

stępujące  operatory  dwuargumentowe: 

=  (równy),  <>  (róŜny,  w  matematyce 

znany jako 

), 

< (mniejszy), > (większy), <= (mniejszy lub równy, w matematy-

ce znany jako 

), 

>= (większy lub równy, w matematyce znany jako 

), 

in (jest 

elementem zbioru, w matematyce znany jako 

). 

Operatory <>, <=, >= naleŜy pisać bez spacji między znakami!  

Wynikiem wyraŜenia porównania jest wartość 

True, gdy relacja jest praw-

dziwa oraz wartość 

False, gdy relacja jest fałszywa. 

Za pomocą operatorów =, 

<>, <, >, <=, >= moŜna porównywać dwa argu-

menty  o  zgodnych  typach  prostych  albo  liczbę  całkowitą  z  liczbą  rzeczywistą 
(np.: 5 = 5 

 

True

93.52 > 4 

 

False, 0 >= 0.0 

 

True, 7 <> 6.9 

 

True). 

Za pomocą operatorów =, 

<>, <, >, <=, >= moŜna porównywać dwa argu-

menty  typu  łańcuchowego  (

ShortString,  string)  lub  znakowego  (Char),  przy 

czym jeden argument moŜe być typu łańcuchowego, a drugi 

 znakowego. Znak 

jest  traktowany  jak  łańcuch  o  długości  1.  Porównanie  zachodzi  pomiędzy  zna-
kami  znajdującymi  się  na  tej  samej  pozycji  w  obu  łańcuchach.  Wartość  znaku 
jest  równa  jego  wartości  w  rozszerzonej  tabeli  kodów  ASCII. 

Wynik  relacji 

porównania  dwóch  łańcuchów  jest  równy  wynikowi  porównania  pierwszej 
(licz
ąc od początku łańcucha) pary róŜnych znaków, stojących na identycz-
nych pozycjach.
 Pusty znak jest mniejszy od kaŜdego innego znaku. 
 

background image

53 

 

Przykłady porównań łańcuchów: 

'informatyk' < 'informatyka'

 

 

True 

'400' = '400.0'

 

 

False 

'b205' >= 'B206'

 

 

True 

 
Na operandach a i b typu zbiorowego moŜna wykonywać operacje:  

=<>

<=>=in. WyraŜenie 
a 

= b   ma wartość True, jeŜeli oba zbiory zawierają te same elementy, 

a 

<> ma wartość True, jeŜeli zbiory róŜnią się co najmniej jednym elementem, 

a 

<= b ma wartość True, jeŜeli zbiór a zawiera się w zbiorze b, 

a 

>= b ma wartość True, jeŜeli zbiór b zawiera się w zbiorze a

c 

in a  ma wartość True, jeŜeli element c jest elementem zbioru a

 

Operatory teoriomnogościowe +, 

 i * (tab. 3.10) słuŜą do wykonywania 

operacji na argumentach typu zbiorowego o zgodnych typach bazowych. 
 

Tabela 3.10. Operatory teoriomnogościowe 

Operator  Nazwa operacji 

Definicja operacji 

suma zbiorów 

(A 

 B) 

c jest elementem zbioru A + B, 

jeŜeli jest elementem zbioru A 

lub jest elementem zbioru B 

 

róŜnica zbiorów 

( A \ B ) 

c jest elementem zbioru A 

 B, 

jeŜeli jest elementem zbioru A 

i nie jest elementem zbioru B 

iloczyn zbiorów 

( A 

 B ) 

c jest elementem zbioru A * B, 

jeŜeli jest elementem zbioru A 

i jest elementem zbioru B 

c  

  element typu porządkowego 

 
Przykłady operacji na zbiorach: 

[10, 12, 14, 8] + [9, 8, 10, 14] 

 [8, 9, 10, 12, 14] 

[10, 12, 14, 8] 

 [9, 8, 10, 14] 

 [12] 

[10, 12, 14, 8] * [9, 8, 10, 14] 

 [8, 10, 14] 

[20..99] 

 [10..40] 

 [41..99] 

['a', 'b'] + ['b', 'c', 'd'] 

 ['a', 'b', 'c', 'd'] 

 

Operator konkatenacji + uŜyty po kaŜdym operandzie (z wyjątkiem ostat-

niego)  słuŜy  do  połączenia  dwóch  lub  więcej  łańcuchów  w  jeden  dłuŜszy  łań-
cuch. Argumenty muszą być typu łańcuchowego lub znakowego. JeŜeli wszyst-

background image

54 

 

kie  operandy  są  łańcuchami  krótkimi  (

ShortString), wówczas  łańcuch  wyniko-

wy równieŜ jest łańcuchem krótkim. JeŜeli łańcuch powstały z połączenia łańcu-
chów krótkich zawiera więcej niŜ 255 znaków, to zostanie obcięty po 255 znaku. 
JeŜeli  co  najmniej  jeden  łańcuch  jest  łańcuchem  długim  (

string),  to  wynik  teŜ 

jest tego typu. Przykład:  ’Lazarus’ + ’ ’ + ’FPC’ 

 ’Lazarus FPC’. 

 

3.8.

 

Priorytet operatorów

 

JeŜeli  w  wyraŜeniu  występuje  większa  liczba  operatorów  i  operandów, 

to w celu określenia jednoznacznej kolejności wykonywania działań, twórcy ję-
zyka  Pascal  wprowadzili  pewien 

priorytet  operatorów,  czyli  hierarchię  ich 

waŜności  (tab.  3.11).  Aby  wymusić  inną  kolejność  operacji,  niŜ  wynika  ona  z 
priorytetu operatorów, stosuje się nawiasy okrągłe, podobnie jak w matematyce. 
 

Tabela 3.11. Priorytet operatorów (od najwyŜszego do najniŜszego) 

Operatory 

Kategoria operatorów 

+, 

, @, 

not 

jednoargumentowe 

*, /, 

divmodandshlshras 

multiplikatywne 

+, 

orxor 

addytywne 

=<><><=>=inis 

relacyjne 

 

Obowiązują następujące zasady wiązania (przez kompilator) operandów do 

operatorów: 

 

argument  występujący  między  dwoma  operatorami  o  róŜnych  prioryte-
tach jest wiązany z operatorem o wyŜszym priorytecie, 

 

argument znajdujący się między dwoma operatorami o tym samym prio-
rytecie jest wiązany z operatorem stojącym z lewej strony. 

Operacje  z  równymi  priorytetami  są  wykonywane  kolejno  od lewej  strony 

do prawej, chociaŜ kompilator moŜe czasami, bez ostrzeŜenia, przestawić argu-
menty w celu wygenerowania optymalnego kodu wynikowego. 
 

3.9.

 

Struktura programu

 

Program  napisany  w  języku  Free  Pascal  składa  się  z  nagłówka,  deklaracji 

uŜywanych w programie modułów, bloku i znaku kropki: 

 

w nagłówku podaje się nazwę programu, 

 

następnie deklaruje się moduły standardowe i uŜytkownika, 

 

blok składa się z opisu danych i części wykonawczej, 

 

kropka kończy tekst programu. 

background image

55 

 

program   nazwa_programu
    część deklaracyjna 
    część opisowa 
begin 
    część wykonywalna 
end
 
KaŜda  instrukcja  programu  jest  zakończona  średnikiem.  Zazwyczaj  układ 

tekstu w linii czy teŜ rozłoŜenie go na kilka linii nie ma znaczenia.  

Część  opisowa  zawiera  deklaracje  typów,  stałych,  zmiennych,  definicje 

i deklaracje procedur i funkcji. Część wykonywalna – instrukcje do wykonania. 
 
Przykład 1. Elementarny program wypisujący tekst na ekranie. 

 
program wyklad02; 
const
  max = 10; 
var  liczba : Byte
 
BEGIN 
  writeln('Pocz

ą

tek'); 

  liczba := 5 + max; 
  writeln('liczba =', liczba); 
  writeln('Koniec'); 
  readln; 
END

 
Przykład 2. Program pokazuje uŜycie typów prostych. 

 
program TypyProstePorz; 
var 
  ok : Boolean
  x : 1..4; 
  dzien : (pon, wt, sr); 
 
BEGIN  
  writeln(SizeOf(int64)); 
  writeln(low(int64)); 
  dzien := wt; 
  writeln(ord(dzien)); 
  inc(dzien); 
  writeln(ord(dzien)); 
  writeln(dzien=sr); 
  x := 4; 

background image

56 

 

  writeln(x); 
  dec(x); 
  writeln(x); 
  dec(x); 
  writeln(x); 
  writeln(high(x)); 
  writeln(low(x)); 
  readln; 
END

 

Przykład 3. Program ilustruje sposób uŜycia zmiennych typu zbiorowego. 

 
program  Typ_zbiorowy; 
type
 zbior = set of Byte
var  x, z : zbior; 
     y : set of 0..9; 
     i : Integer
 
BEGIN 
  x := [1,9,100,10];  {utworzenie zbioru} 
  i := 9; 
  y := [1,2,3,9,i+2]; {liczba 11 zostanie zignorowana} 
  z := [2,4,6,8]; 

  writeln(i,' ');  // 

u

Ŝ

ycie typu zbiorowego 

  if not i in [1..5] then writeln ('tak') 
                     else writeln ('nie'); 
  i := i+1; 

  writeln(i); // i 

 10 

  if i in y then writeln ('tak') else writeln ('nie'); 
  readln; 
END

 

 

background image

57 

 

4.

 

Instrukcje 

 

Operacje  wykonywane  na  danych  są  w  programie  opisywane  za  pomocą 

instrukcji. Formalnie instrukcje dzieli się na proste i strukturalne. 

Do instrukcji prostych zalicza się: 

 

instrukcję przypisania, 

 

instrukcję skoku, 

 

instrukcję pustą, 

 

instrukcję wywołania procedury. 

Do instrukcji strukturalnych naleŜą: 

 

instrukcja złoŜona, 

 

instrukcja warunkowa, 

 

instrukcja iteracyjna, 

 

instrukcja wyboru, 

 

instrukcja wiąŜąca. 

 

4.1.

 

Instrukcje proste

 

Do  przypisania  zmiennej  wartości  innej  zmiennej  lub  wyraŜenia  słuŜy 

instrukcja  przypisania,  wykorzystująca  symbol  :=  (dwukropek  i  znak  równo-
ś

ci).  Wartość  wyraŜenia  występującego  po  prawej  stronie  symbolu  przypisania 

musi być takiego samego typu, jak zmienna, do której następuje przypisanie. 

Przykłady uŜycia instrukcji przypisania: 

liczba := 10; 
przyblizone_pi := 3.14; 
s := 'Komentarz';  
A[1,1] := 22; 

liczba := liczba + 6; // liczba 

 16  

znak := 'h'; 
wiersz[i] := 'H'; 
czy_koniec := True; 
warunek := b*b-4*a*c > 0; 
Wiek := 21; 
P := 'Pierwsza'; 
s := '';  {ła

ń

cuch pusty, brak spacji mi

ę

dzy ''} 

s := ' '; {jedna spacja mi

ę

dzy apostrofami} 

s := 'Witaj'; {s[1] 

 'W', s[1] := 'w'; s 

 'witaj'} 

background image

58 

 

Instrukcja skoku ma postać:  goto etykieta; Zmienną etykieta naleŜy zade-

klarować na początku programu jako: 

label etykieta; a następnie moŜna jej uŜyć 

poprzedzając  wybraną  instrukcję  nazwą  etykiety  i  znakiem  dwukropka,  miano-
wicie: etykietainstrukcja; Instrukcja 

goto etykieta; spowoduje przekazanie ste-

rowania do przodu albo do tyłu w celu pominięcia lub powtórzenia określonego 
zestawu  instrukcji  i  kontynuowania  wykonywania  programu  od  miejsca  ozna-
czonego napisem etykietainstrukcja; UŜywanie instrukcji skoku nie jest zaleca-
ne, gdyŜ  zmniejsza przejrzystość kodu źródłowego programu oraz utrudnia op-
tymalizację kodu przez kompilator. Instrukcja skoku moŜe być łatwo zastąpiona 
bardziej bezpieczną instrukcją iteracyjną. 

Instrukcja  pusta  jest  stosowana  w  sytuacji,  gdy  musimy  uŜyć  jakiejś  in-

strukcji, ale w rzeczywistości nie chcemy nic obliczać. W Pascalu symbol śred-
nika oznacza czasem instrukcję pustą 

 instrukcja ta nie ma swojego symbolu. 

Do wywołania procedury słuŜy instrukcja

 

nazwa_procedury;    albo   nazwa_procedury(lista_parametrów_aktualnych); 

 

Parametry aktualne, czyli argumenty procedury, oddziela się przecinkami. 

Przykłady wykorzystania popularnych procedur standardowych: 

Writeln;    // wypisuje pusty wiersz (pust

ą

 lini

ę

Write;      // wypisuje pusty znak 
Write('a'); // wypisuje znak a 
Write(a);   // wypisuje warto

ść

 zmiennej a  

Writeln(x,2,y);  {  wypisuje  warto

ść

  zmiennej  x,liczb

ę

  2,  

warto

ść

 zmiennej y i przenosi kursor do nast

ę

pnej linii } 

Read;    // czyta pusty znak z klawiatury 
Readln;  // czyta „Enter” - przechodzi do nast

ę

pnej linii 

Read(a); // czyta z klawiatury warto

ść

 zmiennej a 

Readln(a);  {  czyta  z  klawiatury  warto

ść

  zmiennej  a  i  prze-

chodzi do nast

ę

pnej linii } 

 

4.2.

 

Instrukcja złoŜona

 

Instrukcja  złoŜona  (blok  instrukcji)  jest  ciągiem  instrukcji  poprzedzo-

nych słowem kluczowym 

begin i zakończonych słowem kluczowym end

begin 
   instrukcja_1; 
   instrukcja_2; 
   … 
   instrukcja_n 
end 

background image

59 

 

Przykład instrukcji złoŜonej: 

begin 
  x := 3; 
  y := x+b; 
  z := x+y+z; 
  writeln(z); 
end 

 

4.3.

 

Instrukcja warunkowa

 

 

Instrukcja warunkowa uzaleŜnia wykonanie pewnej instrukcji od spełnienia 

podanego warunku. Występuje w dwóch wersjach: pełnej (tzw. 

if 

−−−−

 then 

−−−−

 else

oraz skróconej (tzw. 

if 

−−−−

 then). 

Wersja pełna instrukcji warunkowej ma postać: 
 
if  warunek  then  instrukcja_1    // tu nie daje się średnika 
                   

else   instrukcja_2; 

 
Oznacza to, Ŝe jeŜeli spełniony jest warunek, wówczas zostanie wykonana 

instrukcja_1, w przeciwnym wypadku zostanie wykonana instrukcja_2. 

Wersja skrócona instrukcji warunkowej ma postać: 
 
if  warunek  then  instrukcja
 
Oznacza to, Ŝe jeŜeli spełniony jest warunek, wówczas zostanie wykonana 

instrukcja, w przeciwnym wypadku instrukcja zostanie pominięta. 

 

a) 

         b) 

 

Rys. 4.1. Graficzna postać instrukcji warunkowej: 

a) w wersji pełnej,  b) w wersji skróconej 

 

NIE 

TAK 

warunek 

instrukcja 

NIE 

TAK 

warunek 

instrukcja_1 

instrukcja_2 

background image

60 

 

WyraŜenie warunek  musi być typu logicznego (

Boolean), tzn. musi przyj-

mować wartość 

True albo False.  

Przykłady uŜycia instrukcji warunkowej: 

if a > 5 then b := a-5; 
 
if (a > 0) and (a <= 10) then x := 5 else x := a+5; 
 
if (dzielnik <> 0) then wynik := dzielna/dzielnik 
else writeln('Nie mo

Ŝ

na dzieli

ć

 przez zero!'); 

 
if (dzielnik <> 0) then  
begin  
  wynik := dzielna/dzielnik; 
  writeln('dzielenie wykonano'); 
end 
else
 writeln('Nie mo

Ŝ

na dzieli

ć

 przez zero!'); 

 
if zn in ['0'..'9'] then writeln('zn jest cyfr

ą

'); 

 
if not x in ['0'..'9'] then write('x nie jest cyfr

ą

'); 

 
if a = b then 
  begin  
    writeln('a i b s

ą

 takie same'); 

    obwod := 4*a; 
  end

 
Przykład 1. Program sprawdza, czy liczba jest większa od 100. 

 
program  prog01; 
var
 liczba : Integer
BEGIN  
  write('Prosz

ę

 poda

ć

 liczb

ę

 całkowit

ą

: '); 

  readln(liczba); 
  if liczba > 100 then  
       writeln('Liczba jest wi

ę

ksza od 100.') 

                  else  
       writeln('Liczba jest mniejsza lub równa 100.'); 
  readln; 
END

 
Przykład 2. Program szuka maksimum wśród 3 liczb rzeczywistych. 

 
program  instr_if_1; 

background image

61 

 

var a, b, c, max : Single
BEGIN  
  write('Podaj a ');   readln(a); 
  write('Podaj b ');   readln(b); 
  write('Podaj c ');   readln(c); 
  if a >= b then if a >= c then max := a  
                           else max := c  
            else if b >= c then max := b  
                           else max := c; 
  write('max(a,b,c) = ', max:4:1);  
  readln; 
END

 
Przykład 3. Program zamienia ocenę numeryczną na ocenę tekstową. 

 
Program Oceny; 
var
  ocena : Byte
BEGIN  
 write('Podaj ocen

ę

 '); 

 readln(ocena); 
 if ocena=6 then writeln('celuj

ą

cy') 

  else if ocena=5 then writeln('bardzo dobry') 
    else if ocena=4 then writeln('dobry') 
      else if ocena=3 then writeln('dostateczny') 
        else if ocena=2 then writeln('mierny') 
         else if ocena=1 then writeln('niedostateczny') 
           else writeln('Nieprawidłowa ocena!'); 
 readln; 
END

 

4.4.

 

Instrukcja wyboru  

W  programowaniu  często  mamy  do  czynienia  z  sytuacją,  gdy  wykonanie 

róŜnych operacji jest uzaleŜnione od wartości pewnej zmiennej. Stosuje się wte-
dy instrukcję wyboru (tzw. 

case – of). Składnia instrukcji wyboru ma postać: 

 

case wyraŜenie of 
   sekwencja_instrukcji_wyboru 
end 

albo 

case wyraŜenie of 
   sekwencja_instrukcji_wyboru 
   

else instrukcja 

end 

background image

62 

 

Występujące  w  tej  instrukcji  wyraŜenie  nazywane  jest  selektorem.  Jego 

wartość musi być typu porządkowego. Sekwencja instrukcji wyboru zbudowana 
jest z ciągu instrukcji. KaŜda z nich poprzedzona jest jedną lub kilkoma stałymi 
wyboru, oddzielonymi od siebie przecinkiem 

 po nich występuje dwukropek.  

 

 

Rys. 4.2. Graficzna postać instrukcji wyboru 

 
Przykład 1. Program wczytuje znak i wypisuje jego rodzaj. 
 

program  instr_case1; 
var
  znak : Char
BEGIN  
  write('Prosz

ę

 wpisa

ć

 dowolny znak: '); 

  readln(znak); 
  case znak of 
    '0' .. '9' : writeln('Wpisano cyfr

ę

!'); 

    'A' .. 'Z' : writeln('Wpisano wielk

ą

 liter

ę

!'); 

    'a' .. 'z' : writeln('Wpisano mał

ą

 liter

ę

!'); 

           '=' : writeln('Wpisano znak równo

ś

ci!'); 

       '+','-' : writeln('Wpisano plus lub minus!'); 
  else 
    writeln('Wpisano inny znak!') 
  end; 
  readln; 
END. 

 
Przykład 2. Program zamienia ocenę numeryczną na ocenę tekstową. 
 

program  instr_case2; 

opcja_1 : ci

ą

g instrukcji_1  

case 

wyra

Ŝ

enie 

 of  

ci

ą

g instrukcji 

end  

else 

opcja_2 : ci

ą

g instrukcji_2  

opcja_N : ci

ą

g instrukcji_N  

background image

63 

 

var  ocena : Byte
BEGIN  
  
write('Podaj ocen

ę

'); 

  readln(ocena); 
  case ocena of 
    6: writeln('celuj

ą

cy'); 

    5: writeln('bardzo dobry'); 
    4: writeln('dobry'); 
    3: writeln('dostateczny'); 
    2: writeln('mierny'); 
    1: writeln('niedostateczny') 
  end
  readln; 
END. 
 

4.5.

 

Instrukcje iteracyjne

 

Instrukcja iteracyjna (pętla) stosowana jest w celu wielokrotnego wyko-

nania jednej lub więcej instrukcji. Są dostępne dwa rodzaje pętli: 

 

pętle wykonujące się określoną liczbę razy, 

 

pętle wykonujące się do momentu spełnienia pewnego warunku. 

 

Pętla „

for” (instrukcja dlawykonuje się określoną liczbę razy i występu-

je w dwóch wersjach: 
for – to – do         

(pętla licząca „w górę, aŜ do”)   oraz 

for – downto – do 

(pętla licząca „w dół, aŜ do”). 

 
Pełna składnia obu typów pętli „

for” ma postać: 

 
for   licznik_pętli := początek          to    koniec    do   zawartość_pętli
  
for   licznik_pętli := koniec    downto  początek   do   zawartość_pętli;  
 
NajwaŜniejszy cechy pętli „

for” to: 

 

zmienna sterująca  licznik_pętli  musi być typu porządkowego, 

 

zmienną  licznik_pętli  naleŜy wcześniej zadeklarować, 

 

wartości początek i koniec muszą być tego samego typu, co licznik_pętli

 

literały początek i koniec mogą być stałymi lub wyraŜeniami, 

 

licznik_pętli  zwiększa swą wartość o 1 po kaŜdej iteracji pętli  

to, 

 

licznik_pętli  zmniejsza swą wartość o 1 po kaŜdej iteracji pętli  

downto, 

 

jeŜeli  koniec = początek, to zawartość_pętli wykona się tylko jeden raz,  

background image

64 

 

 

jeŜeli  koniec > początek, to zawartość_pętli  wykona się 
koniec 

 początek + 1  razy,  

 

jeŜeli  koniec < początek, to zawartość_pętli  nie wykona się ani razu, 

 

jeŜeli  zawartość_pętli  zbudowana jest z kilku instrukcji, wtedy naleŜy 
zgrupować je za pomocą  

begin ... end. 

 

Lazarus FPC, w instrukcji zawartość_pętli, nie moŜe wystąpić instrukcja 

przypisania do zmiennej licznik_pętli.  Na przykład nieprawidłowa jest pętla: 
for  m := 1  to  10  do  m := m+2; 
 
Przykład 1. Program oblicza silnię. 
 

program  prog6; 
var
  liczba : Integer
     silnia, i : Longint; 
BEGIN 
  write('Podaj liczb

ę

 naturaln

ą

:'); 

  readln(liczba); 
  silnia := 1; 
  for i := 1 to liczba do silnia := silnia*i; 
  writeln('Warto

ść

 silni dla ',liczba:2,' wynosi',silnia); 

  readln; 
END. 

 

Pętle, których działanie zaleŜy od warunku to: 

while 

 

do („dopóki 

 wykonuj”) oraz  

repeat 

 

until („powtarzaj, aŜ do”). 

Pętla 

while 

 

do słuŜy  do opisu  iteracji  ze  sprawdzeniem  warunku  na  po-

czątku i ma postać: 

 

while  warunek  do  instrukcja

 

Cechy pętli  

while 

 

do

 

warunek jest najczęściej wyraŜeniem porównania, 

 

warunek musi dawać wartość logiczną 

True lub False

 

jeŜeli przed rozpoczęciem wykonywania pętli warunek ma wartość 

Fal-

se, wtedy instrukcja nie wykona się ani razu, 

 

instrukcja moŜe być dowolną instrukcją prostą lub strukturalną, 

 

jeŜeli  pętla  ma  wykonywać  kilka  instrukcji,  to  naleŜy  je  zgrupować  za 
pomocą 

begin … end

 

instrukcja  jest  wykonywana  tak  długo,  jak  długo  wartość  warunku  jest 
równa 

True

background image

65 

 

 

przy korzystaniu z tej pętli naleŜy zwracać uwagę na to, aby miała pra-
widłowo  określony  warunek,  tzn.  Ŝeby  prędzej  czy  później  przyjął  on 
wartość 

False, w przeciwnym razie pętla nigdy się nie przerwie (chyba, 

Ŝ

e zastosujemy procedurę 

Break). 

 

 

Rys. 4.3. Graficzna postać instrukcji iteracyjnej while 

 do 

 

Przykład 2. Program wypisuje liczby: 0, 1, 2, …, 9. 
 

program prog7; 
var
  x : Integer
BEGIN 
  x := 0; 
  while x < 10 do 
    begin 
      writeln(x); 
      x := x + 1; 
    end
  readln 
END. 

 
Przykłady 3. Program oblicza, ile co najmniej trzeba dodać kolejnych liczb na-
turalnych, aby ich suma była większa od podanej liczby. 
 

program prog8; 
var
  n, licznik, suma : Smallint
BEGIN 
  write('Podaj liczb

ę

 całkowit

ą

 '); 

  readln(n); 
  licznik := 0; 
  suma := 0; 
  while suma <= n do 
    begin 
      licznik := licznik + 1; 

NIE 

TAK 

warunek 

instrukcja 

background image

66 

 

      suma := suma + licznik; 
    end
  writeln('Minimalna ilo

ść

 liczb to ', licznik); 

  readln; 
END. 

 
Przykład 4. Program oblicza NWD dwóch liczb metodą z odejmowaniem. 
 

program prog1_NWD; 
var
  n, m : Smallint
BEGIN 
  write('Podaj liczb

ę

 całkowit

ą

 m: '); 

  readln(m); 
  write('Podaj liczb

ę

 całkowit

ą

 n: '); 

  readln(n); 
  while not (m = n) do 
     if m > n then  m := m-n 
              else  n := n-m; 
  writeln(' NWD wynosi: ', n); 
  readln; 
END. 

 

 

 

Rys. 4.4. Schemat blokowy algorytmu wyznaczania NWD metodą kolejnych dzieleń 

 

 

NIE 

TAK 

mod k 

 0 

lub 

mod k 

 0 

 k 

 1 

STOP 

START 

wczytaj  m, n 

 m 

wypisz  k 

background image

67 

 

Przykład 5. Program oblicza największy wspólny dzielnik według rys. 4.4. 
 

program prog92_NWD; 
var  m, n, k : Integer
BEGIN  
  
writeln('Podaj m i n: '); 
  readln(m, n); 
  k := m; 
  while (n mod k <> 0) or (m mod k <> 0) do k := k-1; 
  writeln(k); 
  readln; 
END. 

 

Pętla 

repeat 

 

until  słuŜy  do  opisu  iteracji  ze  sprawdzeniem  warunku  na 

końcu i ma postać: 

repeat 

instrukcja_1; 
instrukcja_2; 
 … 
instrukcja_n 

until  warunek
 

Cechy pętli  

repeat 

 

until

 

instrukcje wewnątrz pętli są wykonywane co najmniej 1 raz, 

 

po kaŜdym wykonaniu instrukcji_n następuje sprawdzenie warunku

 

instrukcje wewnątrz pętli wykonywane są tak długo, jak długo warunek 
ma wartość 

False

 

zakończenie pętli następuje wtedy, gdy warunek ma wartość 

True

 

dla zapewnienia wyjścia z pętli trzeba zadbać o to, aby warunek chociaŜ 
raz osiągnął wartość 

True, 

 

wcześniejsze zakończenie pętli jest moŜliwe przy uŜyciu 

Break

 

 

Rys. 4.5. Schemat blokowy instrukcji iteracyjnej repeat 

 until  

TAK 

NIE 

warunek 

instrukcja  

background image

68 

 

Przykład 6.  Program oblicza n

k

 

program prog10; 
var  
  i, k : Integer
  n, x : Double
BEGIN  
  write('Podaj podstaw

ę

  n: '); 

  readln(n); 
  write('Podaj wykładnik k: '); 
  readln(k); 
  x := 1; 
  i := 1; 
  repeat  
    x := x*n; 
    i := i+1; 
  until i > k; 
  writeln('k-ta pot

ę

ga liczby n wynosi: ', x); 

  readln; 
END. 

 

Przykład 7. Program czyta znaki z klawiatury i podaje ich kod ASCII. 
 

Program prog12a; 
var
  zn : Char
BEGIN  
  repeat  
    readln(zn); 
    writeln(zn, ' ', byte(zn)); 
  until  zn = #13; 
  writeln('Był Enter!'); 
  readln; 
END.

 

 
Przykład 8. 
Program czyta znaki z klawiatury i przerywa działanie, jeŜeli wczy-

ta liczbę. 

 

Program prog12b; 
var
  zn : Char
BEGIN  
  repeat  
    readln(zn); 
    writeln(zn, ' ', byte(zn)); 
  until  (zn >= '0') and (zn <= '9'); 

background image

69 

 

  writeln('Była liczba!'); 
  readln; 
END. 

 
Przykład 9.  Ilustracja nieprawidłowego uŜycia pętli. 
 

Program prog13; 
var zn : Char
  k, x : Byte
BEGIN  
  k := 5; 
  repeat 
    writeln(k); 
    k := k-1; 
  until k > 5; //warunek nigdy nie zostanie spełniony 
 
  k := 5; 
  x := 0; 
  repeat 
    writeln(k); 
    x := x+k; 
  until k < 0; //warunek nigdy nie zostanie spełniony 
  readln; 
END.

 

 

Tabela 4.1. Zastosowanie trzech rodzajów pętli do tego samego zadania 

Pętla  

for

−−−−

to

−−−−

do 

Pętla  

repeat

−−−−

until 

Pętla  

while

−−−−

do 

program p16; 
const  n=7;  
var  s,licznik:longint; 
 

begin  

  s := 1; 
  

for licznik:=2 to n do  

            s:=s*licznik; 
   write(' silnia= ', 

s); 

end. 
 

program p17; 
const  n=7;  
var  s, licznik: Longint; 
 

begin  

   

 s := 1; 

    licznik:=1; 
   

 repeat  

       s := s*licznik; 
       licznik 

:= licznik+1; 

    

until licznik>n; 

    write(' silnia= ', 

s); 

end. 

program p18; 
const  n=7;  
var s,licznik:longint; 
 

begin  

    s:=1; 
    licznik:=2; 
    

while licznik<=n do 

     

begin  

       s := s*licznik; 
       licznik := licznik+1; 
     end
    write(' silnia= ', 

s); 

end. 

 
 

background image

70 

 

Przy wyborze pętli warto kierować się poniŜszymi wskazówkami: 

 

jeŜeli instrukcja ma być wykonana określoną liczbę razy i zmienna ste-
rująca pętli ma się zwiększać, naleŜy wybrać pętlę 

for 

−−−−

 to 

−−−−

 do,  

 

jeŜeli instrukcja ma być wykonana określoną liczbę razy i zmienna ste-
rująca pętli ma się zmniejszać, naleŜy wybrać pętlę 

for 

−−−−

 downto 

−−−−

 do

 

jeŜeli  warunkiem  przerwania  pętli  jest  wyraŜenie  logiczne  i  instrukcja 
ma być wykonana  przynajmniej raz, naleŜy wybrać 

repeat 

−−−−

 until.  

 

jeŜeli warunkiem przerwania pętli jest wyraŜenie logiczne i nic nie wia-
domo o pierwszym  wykonaniu instrukcji, naleŜy wybrać 

while 

−−−−

 do.  

 

jeŜeli nie wiadomo, którą pętlę wybrać, wtedy skuteczna będzie 

while

 

4.6.

 

Procedury Break i Continue 

Czasem moŜe zajść potrzeba natychmiastowego przerwania bieŜącej iteracji 

pętli bez wykonywania instrukcji, które pozostały do końca tej iteracji i zakoń-
czenia wykonywania pętli w ogóle. W inny przypadku moŜe zajść potrzeba na-
tychmiastowego  przerwania  bieŜącej  iteracji  i  rozpoczęcia  następnej.  Do  tych 
obu  celów  słuŜą 

  odpowiednio 

  procedury  standardowe 

Break  i  Continue

Są one zdefiniowane w module 

System

Przykładowe uŜycie procedury 

Continue

 
while  warunek_1  do  
  

begin  

     Instrukcja_1; 
     

if  warunek_2  then  Continue

     Instrukcja_2; 
     Instrukcja_3; 
   

end; 

 

JeŜeli warunek_2 będzie mieć wartość logiczną 

True, wtedy wszystkie in-

strukcje w danej iteracji, występujące po wywołaniu procedury 

Continue, zosta-

ną pominięte i rozpocznie się kolejna iteracja pętli. 
 

Przykładowe uŜycie procedury 

Break

 
while  warunek_1  do  
  

begin  

     Instrukcja_1; 
     

if  warunek_2  then  Break

     Instrukcja_2; 

background image

71 

 

     Instrukcja_3; 
   

end; 

Instrukcja_4; 
 
JeŜeli  warunek_2  będzie    mieć  wartość  logiczną 

True,  wtedy  nastąpi  na-

tychmiastowe przerwanie procesu  iteracji. Następną wykonaną będzie pierwsza 
instrukcja poza pętlą, czyli Instrukcja_4. 

Procedury 

Break i Continue mogą być wywoływane tylko wewnątrz pętli. 

Mogą być uŜyte wewnątrz kaŜdej instrukcji złoŜonej występującej w pętlach 

for

repeat oraz while. UŜycie tych procedur poza pętlą spowoduje błąd kompilacji. 

 

Tabela 4.2. Przykłady uŜycia procedur Continue oraz Break 

UŜycie procedury  

Continue 

UŜycie procedury  

Break 

program  progr20; 
const
  n = 20; 
var  licznik : Byte; 
BEGIN
 
  //s:=1; 
  licznik := 0; 
  while licznik < n do 
   begin  
    
licznik := licznik+1; 
    if licznik mod 3 = 0  
          then  continue; 
    writeln(licznik); 
   end; 
  readln; 
END. 
 

program  progr21; 
const
  n = 20; 
var  licznik : Byte
BEGIN 
  //s:=1; 
  licznik := 1; 
  while licznik < n do 
   begin  
    
licznik := licznik+1; 
    if licznik mod 5 = 0 
             then  break; 
    writeln(licznik); 
   end; 
  
readln; 
END. 
 

 

4.7.

 

Pętle i tablice 

 

Pętle bardzo często stosuje się do danych typu tablicowego, dlatego poka-

Ŝ

emy kilka przykładowych programów, w których są uŜyte pętle na tablicach. 

 

Przykład 1. Program wpisuje do tablicy znaki o kodach ASCII. 

 
Program tablice_znakow;
 
const max = 58;        // 1-wymiarowa tablica znaków 
type  Tznak = array [1..max] of Char;   
var  znak : Tznak; 
        i : Byte

background image

72 

 

BEGIN 
  for i:=1 to max do  znak[i] := char(i+byte('A')-1); 
  for i:=1 to max do  write(znak[i]); 
  readln; 
END. 

 

Przykład 2.  Program, na róŜne sposoby, wpisuje dane liczbowe do tablic i wy-
pisuje dane z tablic na ekranie. 
 

Program tablice_liczbowe; 
const
 N = 5;                   {tablica 1-wymiarowa
type  TRtab = array [0..N] of Single;     
      TCtab = array [0..N] of Smallint; // zmie

ń

 typ 

var C : TCtab; 
    R : TRtab; 
   Cp : TCtab = (0,-1,4,20,-5,6); 
    i : Byte
BEGIN 
  for i:=0 to N do  C[i]:=i;//wypełnianie automatyczne 
  for i:=0 to N do 
  begin  
    write('R[',i,']='); 
    readln(R[i]);     //czytanie z klawiatury 
  end
 
  for i:=0 to N do 
writeln(C[i]);//wypisanie w kolumnie 
  for i:=0 to N do write(Cp[i]:4);//wypisanie w wierszu 
  for i:=0 to N do writeln(R[i]:5:3); 

  readln; 

 

 

// 

 wypisanie+formatowanie 

END. 

 
Przykład 3. Program wpisuje dane liczbowe do tablicy dwuwymiarowej (macie-
rzy) i wypisuje dane z tablicy na ekranie. 
 

Program macierze; 
uses  SysUtils; 
const N = 4;       {tablica 2-wymiarowa
type  TRMatrix  = array [1..N, 1..N ] of Byte
var   M : TRMatrix; 
      i, j : Byte
BEGIN 
  for  i:=1 to N do 
   for  j:=1 to N do  M[i,j] := i+j-1; 
 
  for  i:=1 to N do
 

background image

73 

 

    begin 
      for  j:=1 to N do write(M[i,j]:4); 
      writeln; 
    end; 
  readln; 
END. 

 
Do odwoływania się do poszczególnych pól rekordu lub pól i metod obiek-

tu słuŜą desygnatory pól i metod, składające się z identyfikatora odpowiedniego 
pola lub metody i nazwy zmiennej rekordowej lub obiektowej. Odwołanie takie 
zwykle wydłuŜa tekst programu. Zastosowanie 

instrukcji wiąŜącej pozwala na 

wygodniejsze odwoływanie się do tych pól i poprawia przejrzystość programu. 
Postać składni instrukcji wiąŜącej jest następująca: 
 
with  lista_zmiennych  do  instrukcja
 
lista_zmiennych
  zawiera  oddzielone  przecinkami  identyfikatory  zmiennych  re-
kordowych lub klasowych, a instrukcja moŜe być dowolną instrukcją prostą lub 
strukturalną. JeŜeli po słowie kluczowym 

do występuje kilka instrukcji, to nale-

Ŝ

y je zgrupować za pomocą 

begin … end

Instrukcja wiąŜąca wiąŜe pola typu rekordowego ze zmienną, do której na-

leŜą. Instrukcja wiąŜąca moŜe być zagnieŜdŜana. 
 
Przykład 4. UŜycie instrukcji wiąŜącej do zmiennych typu rekordowego. 
 

program Dane_rekordy; 
type  Tdane = record 
       Imie : string[20]; 
       Wiek : Byte
       Praca : array [1..10] of string
      end; 
 
var   prac1, prac2 : TDane; 
BEGIN 
  prac1.imie := 'Leon'; 
  prac1.wiek := 33; 
  prac1.praca[1] := 'Zaklad energetyczny'; 
  prac1.praca[2] := 'Firma A'; 
  prac2.imie := 'Daria'; 
  prac2.wiek := 27; 
  prac2.praca[1] := 'S

ą

d rejonowy'; 

  // wypisz dane o pracownikach 
  Write(prac1.imie,  prac1.wiek); 

background image

74 

 

  writeln(prac1.praca[1], prac1.praca[2]); 
  // wypisz dane u

Ŝ

ywaj

ą

c instrukcji with 

  with  prac1, prac2  do 
    
Writeln(imie, wiek, praca[1], praca[2]); 
  readln; 
END. 

 

4.8.

 

Zadania

 

 
Zad. 4.1. Napisać program porządkowania trzech liczb. 

Zad. 4.2. Napisać program zamiany liczby dziesiętnej na szesnastkową. 

Zad. 4.3. Napisać program zamiany liczby ósemkowej na dziesiętną.  

Zad.  4.4.  Napisać  program  wyznaczający  najmniejszą  wspólną  wielokrotność 

(NWW) dwóch liczb naturalnych, zgodnie ze wzorem (m

n)/NWD(m,n). 

Zad. 4.5. Napisać program obliczania pierwiastka kwadratowego z liczby nieu-

jemnej a (

a

x

=

), z zadaną dokładnością eps, iteracyjną metodą Newtona 





=

+

i

i

i

x

x

a

x

2

1

1

.  Wartość 

,  czyli 

1

+

i

x

,  ma  dokładność  eps,  jeŜeli 

eps

x

a

i

<

2

.  

i

 oznacza kolejne przybliŜenie  

Zad. 4.6. Napisać program obliczający sumę szeregu 

=

+

+

=

100

1

2

1

i

i

x

i

s

  (x dowolne). 

Zad. 4.7. Napisać program obliczania wyraŜenia danego wzorem 

L

L

7

5

5

3

3

1

6

6

4

4

2

2

2

π

=

 

Zad. 4.8. Napisać program, który wyznaczy wartość maksymalną i wartość mak-

symalną  modułu  elementów  tablicy 

A. Tablica  ma  N  elementów typu  rze-

czywistego tj. 

A[i

 

R dla i=1, 2, …, N. 

Zad. 4.9. Dla danej tablicy dwuwymiarowej 

A[ij],  i, j = 1..N  wyznaczyć tabli-

cę transponowaną (kolumny zamienione na wiersze). RozwaŜyć dwa przy-
padki:  tablica  inicjalizowana  jest  podczas  deklaracji  w  kodzie  źródłowym 
albo tablica jest wczytywana z klawiatury. 

 

 

background image

75 

 

5.

 

Procedury i funkcje 

 

Podprogram  to  wyodrębniona  część  programu,  stanowiąca  całość,  posia-

dająca  nazwę  i  ustalony  sposób  wymiany  informacji  z  pozostałymi  częściami 
programu. Ze względu na sposób wymiany informacji (lub jego brak) z pozosta-
łą częścią programu, podprogramy dzielimy na procedury i funkcje. 

Funkcje i procedury: 

 

umieszcza się w części deklaracyjnej programu lub w odrębnej jednost-
ce kompilacyjnej, 

 

funkcja  zazwyczaj  operuje  na  pewnych  argumentach  (ale  nie  musi) 
i zwraca pewną, obliczoną wartość (lub zestaw wartości), 

 

funkcja moŜe modyfikować argumenty, chociaŜ zazwyczaj tylko wyko-
rzystuje do obliczeń przekazane jej wartości, 

 

procedura jest podobna do funkcji, z tym, Ŝe nie moŜe zwracać wartości 
w sposób zarezerwowany dla funkcji, 

 

procedura  moŜe  posiadać  argumenty  lub  nie,  moŜe  zwracać  wartości 
do „świata zewnętrznego”, ale tylko w określony sposób. 

 

5.1.

 

Składnia procedury 

Procedura to podprogram wykonujący jedną lub więcej czynności i zwra-

cający od jednego do kilku wyników lub niezwracający Ŝadnego wyniku. Nazwa 
procedury jest uŜywana w charakterze instrukcji w programie. 
Składnia definicji procedury jest następująca: 
 

procedure  nazwa_procedury (lista_parametrów_formalnych);  
   część_deklaracyjna  
begin 
    ciąg_instrukcji 
end; 
 
Część  deklaracyjna  oraz  lista  parametrów formalnych  są opcjonalne,  tzn. 

moŜna ich nie definiować, jeŜeli nie są potrzebne. Część deklaracyjna procedury 
moŜe zawierać te same elementy, co część opisowa programu tzn.: definicje ty-
pów,  stałych,  zmiennych,  ale  nie  klas.  Parametry  formalne  oddziela  się  średni-
kami.  Lista  parametrów  formalnych  określa  równieŜ,  w  jaki  sposób  parametry 
formalne zostaną zastąpione parametrami aktualnymi w trakcie wywołania pro-
cedury. Parametry aktualne w wywołaniu procedury oddziela się przecinkami. 

background image

76 

 

Parametry  formalne  pojawiają  się  w  nagłówku  deklaracji  funkcji  bądź 

procedury  i  są  uŜywane  w  treści  funkcji  lub  procedury  jak  zwykłe  zmienne. 
Liczba parametrów formalnych nie jest ograniczona i ich kolejność występowa-
nia jest dowolna. Parametr formalny jest obowiązkowo zmienną. 

Parametry  aktualne  są  parametrami  występującymi  w  bloku,  z  którego 

wywoływana  jest  funkcja  lub  procedura  i  uŜywane  są  podczas  wywołania  pro-
cedury lub funkcji. Liczba parametrów aktualnych musi być taka sama, jak licz-
ba  parametrów  formalnych  w  nagłówku  i  musi  zachodzić  zgodność  typów. 
Parametrem aktualnym moŜe być wyraŜenie, zmienna albo stała. 
 

 

Rys. 5.1. Schemat blokowy składni  procedury 

 

WaŜne uwagi dotyczące wywoływania (uŜywania) procedur w programie: 

 

wywołanie  procedury  polega  na  uŜyciu  w  programie  jej  nazwy  z  listą 
parametrów aktualnych (jeŜeli istnieje) w nawiasach okrągłych: 
nazwa_procedury (lista_parametrów_aktualnych); 

przykład:  

wypisz (a, b, c); 

 

parametry aktualne oddziela się przecinkami, 

 

typy parametrów aktualnych muszą być zgodne z typami odpowiadają-
cych im parametrów formalnych, 

 

lista parametrów formalnych pozwala w sposób jawny przekazywać da-
ne do procedury lub funkcji. 

  
 

 

nagłówek 

procedury 

begin 

ci

ą

g instrukcji 

 end  

cz

ęść

 

deklaracyjna 

blok 
procedury 

background image

77 

 

5.2.

 

Przekazywanie parametrów do procedur i funkcji 

Zastępowanie parametrów formalnych aktualnymi jest nazywane przekazy-

waniem parametrów do procedury lub funkcji

. WyróŜnia się trzy główne sposo-

by przekazywania parametrów: przez wartości, przez zmienne i przez stałe. 

 
Parametry przekazywane przez wartości deklaruje się w nagłówku pro-

cedury (lub funkcji) następująco:  

procedure  nazwa_procedury 

(Identyfikator_1 : typ_1;  Identyfikator_2 : typ_2; ...; Identyfikator_n : typ_n); 

Przekazywanie 

przez wartość to przekazywanie w jedną stronę, tzn. praca 

na kopii parametru. Parametry formalne przekazywane przez wartości  (Identyfi-
kator

_1, Identyfikator_2 itd.) są, w obrębie procedury (lub funkcji), traktowane 

jak  zmienne  lokalne,  którym  nadaje  się  wartości  początkowe  równe  wyliczo-
nym, w chwili wywołania, wartościom wyraŜeń stanowiących parametry aktual-
ne. 

Operacje wykonywane w treści funkcji lub procedury są wykonywane  

zatem  na  kopii  umieszczonej  na  stosie  i  w  związku  z  tym  nie  powodują 
zmian warto
ści odpowiadających im parametrów aktualnych. 
 
Przykłady deklaracji procedur z parametrami przekazywanymi przez wartości: 

procedure  dodaj (a, b : Integer;  c : Single); 
procedure  generator (x : Real); 
procedure  Drukuj(linia1, linia2 : string); // ShortString jest niedozwolony 
procedure  WypiszDate;   // procedura bez parametrów 
type   Matrix = array [1..5, 1..4]  of  Double;  
procedure  Zapisz(M : Matrix); 
 
Parametry  przekazywane  przez  zmienne  deklaruje  się  w  nagłówku 

procedury (lub funkcji) następująco:  

procedure  nazwa_procedury (var Identy-

fikator

_1 : typ_1; 

var Identyfikator_2 : typ_2; ...; var Identyfikator_n : typ_n); 

Oprócz  standardowych  typów  moŜna  uŜyć  takŜe  słów  kluczowych 

string 

file

Argumenty  aktualne,  odpowiadające  parametrom  formalnym  przeka-

zywanym przez zmienne, muszą być zmiennymi. 

UŜycie  parametru  przekazywanego  przez  zmienną  oznacza  pracę  na 

oryginale  parametru  aktualnego.  Operacje  wykonywane  w  treści  funkcji  lub 
procedury  na  parametrach  przekazywanych  przez  zmienne  są  wykonywane 
(w  momencie  wywołania  procedury  lub  funkcji)  bezpośrednio  na  przekazywa-
nych  do  procedury  lub  funkcji  parametrach  aktualnych. 

JeŜeli  w  treści  proce-

dury  lub  funkcji  zostaną  uŜyte  instrukcje  zmieniające  wartość  parametru 

background image

78 

 

formalnego,  to  identyczne  zmiany  dokonają  się  równieŜ  w  parametrze  ak-
tualnym po wywołaniu procedury lub funkcji.
 
 
Przykłady deklaracji procedur z parametrami przekazywanymi przez zmienną: 

procedure  zapisz(var x : Byte); 
procedure  dodaj(var a : Integer var  Single); 
procedure  dodaj(a, b : Integer var  c : Single); // tylko c przez zmienną 
type  Matrix = array [1..5, 1..5] of Byte;  
procedure  dodajM(A, B : Matrix;   var M : Matrix);  // tylko M 

 

 

Dzięki  parametrom  przekazywanym  przez  zmienne  procedury  mogą

w pewnym sensie, zwracać wartości. 
 

Parametry typu plikowego muszą być przekazywane przez zmienną

 

Dopuszczalne  jest,  aby  część  parametrów  była  przekazywana  przez  war-

tość, a reszta przez zmienne. 
 

Parametry przekazywane przez stałe deklaruje się w nagłówku procedu-

ry  (lub  funkcji)  następująco:   

procedure    nazwa_procedury  (const  Identyfika-

tor

_1 : typ_1; 

const Identyfikator_2 : typ_2; ...; const Identyfikator_n : typ_n); 

Parametry przekazywane przez stałe są w treści procedury lub funkcji 

traktowane  jako  zmienne  lokalne  przeznaczone  tylko  do  odczytu.  W  treści 
procedury lub funkcji nie jest dozwolone przypisywanie im jakichkolwiek war-
tości.  Zastosowanie  instrukcji  przypisania  do  parametru  przekazywanego  przez 
stałą wygeneruje błąd na etapie kompilacji. 

Dla  parametrów  typu  łańcuchowego  i  strukturalnego  zaleca  się  stosować 

przekazywanie przez stałe, gdyŜ kompilator generuje dla nich bardziej efektyw-
ny kod. 
 

Parametry otwarte umoŜliwiają przekazywanie do tej samej procedury lub 

funkcji tablic o róŜnych rozmiarach. Deklaracja tablicowego parametru otwarte-
go ma postać: 

 

var     nazwa_zmiennej : array of  nazwa_typu   lub 
const  nazwa_zmiennej : array of  nazwa_typu   lub 
           nazwa_zmiennej

 : 

array of  nazwa_typu 

 
w  zaleŜności  od  tego,  czy  deklarowany  parametr  jest  przekazywany  przez 
zmienną, stałą czy wartość. 

background image

79 

 

W treści procedury lub funkcji tablicowy parametr otwarty jest traktowany 

jak  

array [0..n-1] of nazwa_typu, gdzie n oznacza liczbę elementów argumentu.  

W  treści  procedury  lub  funkcji,  do  określenia  liczby  elementów  tablicy 

otwartej, stosuje się funkcję standardową 

High. Do tej tablicy moŜna teŜ stoso-

wać funkcje 

SizeOf  i Low

 

Tabela 5.1. Program z definicjami procedur, demonstrujący ich działanie 

program Proc1; 
 
procedure www(k, m : Integer); 
begin     // przekazywanie przez wartości 
    writeln('k=',  k); 
    k := k+m; 
    writeln('k=',  k); 
end; 
 
procedure zzz(var k, m : Integer); 
begin  // przekazywanie przez zmienne 
     writeln('k=',  k); 
     k := k+m; 
     writeln('k=',  k); 
end; 

 

 var     // c.d. 
  a, b : Integer
BEGIN  
   a := 1;   
   b := a+1; 
   www(a, b+1); 
   writeln('a=',  a); 
   www(a, b+1); 
   writeln('a=',  a); 
   zzz(a, b); 
   writeln('a=',  a); 
   zzz(a, b); 
   writeln('a=',  a); 
   readln; 
END

 

 
Przykład 1. Generowanie i wypisywanie zawartości macierzy. 

 

program Proc3; 
uses
 SysUtils; 
const RM = 5; 
type matrix = array [1..RM, 1..RM] of Byte
 
procedure  gener(var M : matrix); 
var  i, k : Byte;  //zmienne lokalne 
begin 
  Randomize; 
  for i:=1 to RM do 
     for k:=1 to RM do 
        M[i,k] := random(10); 
end; 
 
procedure  wypisz
(M : matrix); 
var i, k : Byte
 

background image

80 

 

begin 
  for i:=1 to RM do 
    begin 
      for k:=1 to RM do write(M[i,k]:4); 
      writeln; 
    end; 
  writeln; 
end; 
 
var  A, M : Matrix; 
BEGIN              
  gener(M); 
  wypisz(M); 
  gener(A); 
  wypisz(A); 
  readln; 
END. 

 

Lazarus FPC istnieje moŜliwość podania 

domyślnej wartości parame-

tru  wywołania  procedury  lub  funkcji  przez  uŜycie  słowa  kluczowego  const 
w nagłówku przed nazwą parametru oraz podanie jego wartości po znaku =.  

 

Przykład 3. Procedura z domyślną wartością parametru wywołania. 
 

program Proc2; 
Procedure demo(const napis : string = 'Domyslny'); 
begin 
  writeln(napis); 
end
 
var  s : string
BEGIN 
  s := 'procedura demo'; 
  demo(s); 
  demo; 
  demo('dowolny napis'); 
  readln; 
END

 

5.3.

 

Procedury i dyrektywa zapowiadająca Forward 

Dyrektywy  zapowiadającej 

Forward  uŜywa  się  w  celu  zadeklarowania 

podprogramu, który zostanie zdefiniowany później po to, aby w podprogramach 
zdefiniowanych wcześniej moŜna było uŜyć wywołania tego podprogramu. 

background image

81 

 

   

Przykład uŜycia dyrektywy zapowiadającej Forward
 

Program proc5; 
var
 a : Byte
 
procedure druga (var y : Byte);  forward; 
 
function pierwsza (var x : Byte) : Byte; 
begin  
  inc(x); 
  if x < 10 then druga(x);  // Result := x; 
end; 
 
procedure druga (var y : Byte);  // forward; 
begin 
  pierwsza(y); 
end; 
 
BEGIN 
  a := 4;      // warto

ść

 pocz

ą

tkowa 

  pierwsza(a); 
  writeln(a);  // warto

ść

 ko

ń

cowa 

  readln;  
END. 

 

5.4.

 

Składnia funkcji 

Funkcja to podprogram wykonujący jedną lub więcej czynności i zwraca-

jący jeden typ wartości, ale niekoniecznie tylko jedną wartość. MoŜe to być np. 
tablica  wartości.  Nazwa  funkcji  uŜywana  jest  w  charakterze  wyraŜenia  lub  in-
strukcji. 
 
 

 

background image

82 

 

 

Rys. 5.2. Schemat blokowy składni funkcji 

 

Podstawowa składnia definicji funkcji jest następująca: 

 

function   nazwa_funkcji (lista_parametrów_formalnych)  :  typ_wyniku

// deklaracja lokalnych typów (

type), stałych (const) i zmiennych (var

Begin 
    Ciąg_instrukcji
    Result := wyraŜenie;  // albo  nazwa_funkcji := wyraŜenie
End; 

 

Lista  parametrów  formalnych  moŜe  być  pusta.  Wówczas  nawiasy  okrągłe 

moŜna opuścić albo pozostawić. 

PoniewaŜ 

funkcja zwraca pewną wartość, to w treści funkcji musi wystą-

pić co najmniej jedna instrukcja przypisania postaci: 
nazwa_funkcji

 := wyraŜenie;     albo    

Result := wyraŜenie

która  powoduje  przypisanie  wartości  wyraŜenie  nazwie  funkcji.  Identyfikator 
Result jest  domyślną  zmienną  lokalną  w  funkcji i  nie  ma  potrzeby jej  deklaro-
wać. Typ zmiennej 

Result jest taki sam, jak typ_wyniku

Identyfikator 

Result  moŜe  wystąpić  wiele  razy  w  treści  funkcji,  ale  jako 

wartość zwracana przez funkcję będzie ta, która jako ostatnia została przypisana 
do zmiennej 

Result. NaleŜy tak uŜywać instrukcji warunkowych i pętli w treści 

funkcji, aby w kaŜdej sytuacji, tzn. bez względu na wartości logiczne warunków 
występujących  w  treści  funkcji,  zawsze  była  przypisana  pewna  wartość  do 
zmiennej 

Result.  Tak  jak  kaŜda  zmienna  lokalna,  równieŜ  zmienna  Result  po-

nagłówek 

funkcji 

begin 

ci

ą

g instrukcji 

 end  

cz

ęść

 

deklaracyjna 

blok 
funkcji 

Result 

 wyra

Ŝ

enie 

background image

83 

 

winna  być  zainicjalizowana  przed  pierwszym  uŜyciem,  w  przeciwnym  razie 
wartość tej zmiennej jest nieokreślona (losowa). 

Wykonanie  instrukcji   

Result  :=  wyraŜenie;    nie  kończy  działanie  funkcji, 

lecz  tylko  ustala  wartość  zwracaną  przez  funkcję.  Wszystkie  instrukcje,  które 
znajdują  się  dalej  w  treści  funkcji  są  wykonywane,  aŜ  do  słowa  kluczowego 
end; Wystąpienie instrukcji Result := wyraŜenie_2;  powoduje zmianę wartości 
zwracanej przez funkcję.  

Wywołanie funkcji polega na wpisaniu w kodzie źródłowym programu na-

zwy funkcji z odpowiednią listą parametrów aktualnych: 
 
nazwa_funkcji (lista_parametrów_aktualnych); 
 
Parametry aktualne oddziela się przecinkami. 

JeŜeli typ_wyniku nie zgadza się z typem wartości podstawianej pod zmien-

ną 

Result, wówczas wartość podstawiana jest odpowiednio konwertowana, jeśli 

jest to moŜliwe.  

 

Uwagi odnośnie parametrów formalnych i aktualnych oraz na temat sposo-
bów przekazywania parametrów do funkcji s
ą takie same jak dla procedur. 

 

Przykład funkcji z parametrami a i b, przekazywanymi przez wartość: 
 

function  dodaj (a : Byte;  Single: Single;  
begin 
    

Result := a+b;     // albo   dodaj := a+b; 

end; 
 

Przykład wywołania funkcji 

dodaj

 

var   a, x : Byte
       b, y, wynik : 

Single

... 
wynik := 

dodaj(x, y); 

wynik := 

dodaj(x, dodaj(x, y)); 

 
Przykład ilustrujący przekazywanie tablic do funkcji: 
 

type  Wektor = array [1..10] of  Smallint;  
function  Iloczyn_skalarny (A, B : Wektor) : Smallint;  

 

background image

84 

 

Przykład wywołania funkcji Iloczyn_skalarny: 

var A, B : Tablica; 
   wynik : 

Smallint;     

... 
wynik := 

Iloczyn_skalarny(A, B);    writeln(’wynik=’, wynik); 

//  albo 
writeln(’wynik=’, 

Iloczyn_skalarny(A, B)); 

 
Przykład 1. Wywołanie funkcji z parametrami przekazywanymi przez wartość. 

program Podpr2;  
function suma2(k, m : Integer) : Integer
begin 
  Result := k+m; 
  Result := 2*Result; 
  //suma2 := 2*(k+m); // niepolecane 
end; 
 
var
  a, b, x : Integer
BEGIN 
  x := suma2(1,4);     //wywołanie funkcji suma2 
  writeln('suma=', x); 
  a := 2;  b := 3; 
  writeln(suma2(2,3)); //wywołanie funkcji suma2 
  writeln(suma2(a,b)); //wywołanie funkcji suma2 
  readln; 
END

 
Przykład 2. Funkcja  

wypelniacz

 

 

wypełnia tablicę 

A

 podaną liczbą 

x

program Podpr5; 
type  TArr = array [1..10] of Integer
 
function wypelniacz(x : Integer) : TArr; 
var i : Integer
begin  
  for i := 1 to 10 do Result[i] := x; 
end; 
 
var  A : TArr; 
     i : Byte
BEGIN 
  A := wypelniacz(2); 
  for i := 1 to 10 do write(A[i], ' '); 
  readln; 
END. 

background image

85 

 

5.5.

 

PrzeciąŜanie funkcji 

PrzeciąŜanie  (przeładowywanie)  funkcji  polega  na  definiowaniu  kilku 

funkcji lub procedur o tych samych nazwach róŜniących się zestawem parame-
trów formalnych  wywołania.  W takim  przypadku  trzeba  uŜyć  dyrektywy 

over-

load na końcu nagłówka kaŜdej przeciąŜonej funkcji lub procedury. 

O  tym,  którą  przeciąŜoną  funkcję  wywołać,  decyduje  kompilator,  ba-

dając typy parametrów aktualnych uŜytych w konkretnym wywołaniu. 

Funkcji i procedur o tych samych nazwach i o jednakowych listach parame-

trów formalnych, ale róŜnych typach wartości zwracanej nie wolno przeciąŜać.  

 

Przykład 1. PrzeciąŜanie funkcji 

dodaj

program Podpr4; 
function dodaj(a,b : Byte) : Byte;  Overload
begin 
  Result := a+b; 
end; 
function
 dodaj(a,b : string) : string;  Overload
begin 
  Result := a+b; 
end; 
 
BEGIN  
  writeln('dodawanie liczb =', dodaj( 1, 2 )); 
  writeln('dodawanie łancuchow =', dodaj('1', '2')); 
  readln; 
END. 

 
Przykład 2. Funkcja zamiany liczby naturalnej na kod o podstawie 2

÷

10. 

Program fun_4; 
const
 MaxPodst = 10; 
type  TPodst = 2..MaxPodst; 
 
function Card2Str(x : Cardinal; p : TPodst) : string
type TCyfra = 0..MaxPodst-1; 
var  cyfra : TCyfra; 
begin  
  Result := ''; 
  repeat  
    cyfra := x mod p; 
    x := x div p; 
    Result := char(cyfra + byte('0')) + Result; 
  until x = 0; 
end; 

background image

86 

 

var a : Cardinal;  
    b : TPodst; 
 
BEGIN  
  write('a='); 
  readln(a); 
  write('podstawa systemu liczenia:'); 
  readln(b); 
  writeln(Card2Str(a, b)); 
  readln; 
END. 

 

5.6.

 

Wywołanie funkcji przez inną funkcję 

W kodzie źródłowym funkcji moŜe wystąpić wywołanie innej funkcji. Wy-

konywanie  bieŜącej  funkcji  jest  wówczas  chwilowo  przerywane,  a  rozpoczyna 
się wykonywanie drugiej. Po zakończeniu wykonania funkcji drugiej sterowanie 
wraca do pierwszej funkcji, do miejsca gdzie została przerwana. Tak więc funk-
cje i procedury moŜna zagnieŜdŜać. 

W treści funkcji lub procedury moŜna umieścić wywołanie innej funkcji lub 

procedury, która została zdefiniowana wcześniej. 

W części deklaracyjnej funkcji (procedury) tzw. zewnętrznej moŜna zdefi-

niować inną funkcję (procedurę) tzw. wewnętrzną, która będzie lokalna, tzn. wi-
doczna tylko w treści procedury zewnętrznej. 

 

Wywołanie funkcji 

w treści funkcji B

Definicja funkcji B w czę-
ś

ci deklaracyjnej funkcji A 

i wywołanie funkcji B w 
treści funkcji A

Wywołanie procedury 
w procedurze B

function A:pewien_typ
 begin  
   ...  
 end; 
 
function  B
var  x : pewien_typ
 begin  
   ... 
   x := 

A

  ...

  

end;  

function A
 
 function B:pewien_typ
    begin  
    ...  
   end; 
 
var
  x : pewien_typ
begin  
   ... 
   x := 

B

  ...

  

end; 

procedure A
begin  
   ...  
end; 
 
procedure B
begin  
   ... 
   

A

end;  
 

background image

87 

 

5.7.

 

Zmienne globalne i lokalne 

Zmienne globalne to zmienne zdefiniowane na zewnątrz procedur, funkcji 

oraz  metod.  Zmiennych  globalnych  moŜna  uŜywać  we  wszystkich  funkcjach 
i  procedurach  pliku  źródłowego,  w  którym  zostały  zadeklarowane.  Wszystkie 
zmienne globalne są umieszczane w tzw. segmencie danych, dla którego pamięć 
jest  przydzielana  tylko  raz,  w  chwili  rozpoczęcia  wykonywania  programu. 
Pamięć  ta  jest  zerowana  na  starcie  programu,  tzn. 

zmiennym  globalnym  są 

przypisywane wartości zerowe odpowiedniego typu. Tak więc, jeŜeli progra-
mista  zapomni  zainicjalizować  zmienną  globalną,  to  istnieje  duŜa  szansa,  Ŝe 
program będzie działał prawidłowo, gdyŜ bardzo często pierwszą przypisywaną 
(celowo) wartością do zmiennej globalnej jest właśnie wartość zerowa. 

Powinno  się  unikać  uŜywania  zmiennych  globalnych  w  treści  procedur 

i funkcji, gdyŜ utrudnia to śledzenie poszczególnych fragmentów kodu i popra-
wianie  błędów  (debugowanie).  Zmienną  globalną  powinno  przekazywać  się  do 
procedur lub funkcji: 

 

przez wartość, jeŜeli chcemy działać na kopii zmiennej globalnej, 

 

przez zmienną, jeŜeli chcemy zmienić wartość tej zmiennej globalnej, 

 

przez stałą, jeŜeli chcemy tylko odczytać wartość zmiennej globalnej. 

 
Z

mienne  lokalne  to  wszystkie  pozostałe  zmienne  w  programie,  tzn.  te, 

które nie są globalne. Właściwości zmiennych lokalnych są następujące: 

 

zmienne  lokalne  są  umieszczane  w  tzw.  segmencie  stosowym,  czyli  w 
obszarze pamięci podręcznej (w innym obszarze niŜ zmienne globalne), 

 

przy  kaŜdorazowym  wywołaniu  funkcji  zmiennym  lokalnym  przydzie-
lana  jest  na  stosie  pamięć,  a  po  zakończeniu  wykonywania  funkcji  pa-
mięć ta jest zwalniana, 

 

wewnątrz funkcji moŜna definiować tylko zmienne lokalne, 

 

zmienna lokalna danej funkcji jest widoczna tylko w tej funkcji, w któ-
rej została zadeklarowana, 

 

nie  moŜna  w  jednej funkcji  odwołać  się  do  zmiennej  lokalnej  zadekla-
rowanej w innej funkcji, 

 

wiele funkcji moŜe mieć zmienną lokalną o takiej samej nazwie, 

 

nazwy  zmiennych  lokalnych  i  globalnych  mogą  być  jednakowe  (wtedy 
jednak zmienne lokalne przysłaniają zmienne globalne), 

 

zmienne  lokalne  nie  są  zerowane  przy  „powoływaniu  do  Ŝycia”,  tzn. 
mają  wartość  losową,  która  przy  kaŜdym  kolejnym  uruchomieniu  pro-
gramu moŜe być inna niŜ poprzednio, 

background image

88 

 

 

po  zakończeniu  wykonywania  bloku  instrukcji,  dla  którego  została  po-
wołana do Ŝycia zmienna lokalna, przestaje ona istnieć (jest usuwana ze 
stosu), 

 

przy ponownym wejściu do danego bloku  (np. przy ponownym wywo-
łaniu  tej  samej  funkcji)  zmienna  lokalna  jest  powoływana  do  Ŝycia  po 
raz kolejny, 

 

przerwanie wykonywania jednej funkcji przez wywołanie innej, a nawet 
tej samej, nie powoduje zlikwidowania zmiennych lokalnych pierwszej 
funkcji  znajdujących  się  juŜ  na  stosie.  Oznacza  to,  Ŝe  mogą  istnieć  na 
stosie dwie zmienne lokalne o jednakowej nazwie naleŜące do tej samej 
funkcji, ale posiadające róŜne wartości. Zadaniem kompilatora jest pra-
widłowe rozróŜnienie tych zmiennych.  

 

zmienne  lokalne  nie  mogą  mieć  nazw  takich,  jak  nazwy  parametrów 
formalnych funkcji. 

 

Takie same uwagi odnoszą się do zmiennych lokalnych w procedurach. 
 
Przykład 1.
  Program z jedną zmienną lokalną i jedną zmienną globalną. 

program proc4; 
var  zm_globalna : Integer
 
Procedure  proc_glob; 
var  zm_lok : Integer
begin 
  zm_lok := 1; 
  zm_globalna := zm_globalna+1; 
  writeln('zm_lok=', zm_lok); 
  writeln('zm_glob=', zm_globalna);   
end; 
 
BEGIN  
  zm_globalna := 5; 
  writeln( 'zm_glob=', zm_globalna ); 
  proc_glob; 
  writeln( 'zm_g=', zm_globalna ); // 6 
  zm_globalna := 9; 
  writeln( zm_globalna );  //  9 
  proc_glob;               // 10 
  writeln( zm_globalna );  // 10 
  readln; 
END; 

 
 

background image

89 

 

Przykład 2.  Przysłanianie zamiennych globalnych  przez zmienne lokalne. 

program fun_zmienne; 
var  x, y : Byte;  // zmienne globalne 

 

  Function zewn(a : Byte) : Byte
  var x : Byte;  // zmienna lokalna 
   
   function wewn(a : Byte) : Byte
    var x : Byte; // zmienna lokalna  
      begin  
        x := 1; 
        x := x+1; 
        Result := x; 
      end

 

  Begin // funkcja zewn 
    x := 10; 
    x := x + wewn(a); 
    Result := x; 
  End; 
 
BEGIN
    
  x := 100; 
  y := x + zewn(x);  
  {Jaka  b

ę

dzie  warto

ść

  y?  Za  pomoc

ą

  klawisza  F7  i  okna     

   Czujki nale

Ŝ

y prze

ś

ledzi

ć

 warto

ś

ci zmiennych y i x.

  writeln('Jaki wynik? ..', y); 
  readln; 
END. 

 

Przykład 3.  Przekazywanie parametrów przez zmienną. 

program z1fun; 
var  x, y : Integer

 

Function zad1(a : Integervar b : Integer) : Integer
  function wew1(c, d : Integer) : Integer;  
    begin  
      d := c+d; 
      wew1 := d+x;  // albo  Result := d+x; 
    end;  

 

Begin 
  a := a+b; 
  b := a+b; 
  Result := wew1(a,b); // albo  zad1 := wew1(a,b); 
End

background image

90 

 

BEGIN 
  x := 2; 
  y := 1; 
  writeln(zad1(x, y));  // ? 
  writeln(zad1(y, x));  // ? 
  writeln(x, y);        // x=?,  y=? 
  readln; 
END

 

5.8.

 

Funkcje rekurencyjne

 

Rekurencja polega na tym, Ŝe funkcja moŜe wywoływać samą siebie.  MoŜe 

to robić kaŜda funkcja, ale jeśli nie ma w niej warunku zatrzymującego rekuren-
cję, to będzie się wywoływać w nieskończoność, tzn. aŜ do wyczerpania pamięci 
na  stosie.  Gdy  funkcja  rekurencyjna  wywołuje  samą  siebie,  na  stosie  są  juŜ 
zmienne  lokalne  będące  własnością pierwszego  wywołania  tej funkcji. Kolejne 
jej wywołanie umieszcza na stosie swoje własne zmienne lokalne. 

 

Przykład 1. Procedura wywołująca siebie rekurencyjnie. 

Program procedura_rek; 
var  k : Integer
procedure  inkrementacja(x : Integer); 
begin  
  x := x+1; 
  if  x < 4  then  inkrementacja(x)  else 
  writeln('Koniec wywoływania rekursyjnego dla x =',x); 
  writeln('Jestem ostatnim poleceniem na poziomie ',x); 
end
 
BEGIN  
  k := 0; 
  inkrementacja(k); 
  readln; 
END

 
Przykład 2.
 Rekurencyjne obliczanie silni. 

program silnia_rekurencyjna; 
function  Rsilnia(liczba : Integer) : Cardinal
 begin  
  if liczba=0 then Rsilnia := 1 
              else Rsilnia := liczba*Rsilnia(liczba-1); 
 end
 
var  n : Byte

background image

91 

 

BEGIN 
  writeln('Podaj n='); 
  readln(n); 
  writeln(' n! = ', Rsilnia(n)); 
  readln; 
END. 

 

Przykład 3. Rekurencyjne obliczanie kolejnych liczb ciągu Fibonacciego. 

program FibRekur;   // F

0

=0,  F

1

=1,  F

n

 = F

n-1

 + F

n-2

 

function Fib(n : Cardinal) : Cardinal
  begin  
   if n <= 1 then  Result := n 
             else  Result := Fib(n-1) + Fib(n-2); 
  end
var    i : Integer
BEGIN 
  for i := 0 to 40 do writeln(i:4, ' : ', Fib(i)); 
  readln; 
END. 

 

5.9.

 

Typ funkcyjny 

Typ funkcyjny (proceduralny) deklarowany słowami kluczowymi 

type oraz 

procedure  lub  function  stworzony  został  po  to,  aby  traktować  procedury  lub 
funkcje jako wartości pewnych zmiennych.  

type  MojaFunkcja =  function (a, b : byte) : real; 

W  przedstawionym  przykładzie  wartość  zmiennej  typu  proceduralnego 

MojaFunkcja staje się funkcją realizującą zadania wybrane przez uŜytkownika.  

Typy i zmienne proceduralne moŜna równieŜ definiować w sekcji deklara-

cji zmiennych, np.:    

var  MojaFunkcja :  function (a, b : byte) : real

Przykład: 

program  typ_procedur2; 
 type  TFunkcja = function(a,b : Integer) : Real; 
    
 function dodaj(skladnik1, skladnik2 : Integer) : Real
   begin  
     Result := skladnik1 + skladnik2; 
   end
 
 function odejmij(sklad1, sklad2 : Integer) : Real
   begin  
     Result := sklad1 - sklad2; 
   end

background image

92 

 

 
 function pomnoz(czynnik1, czynnik2 : Integer) : Real
   begin  
     Result := czynnik1 * czynnik2; 
   end
 
 function podziel(dzielna, dzielnik : Integer) : Real
   begin  
     if dzielnik<>0 then Result := dzielna / dzielnik; 
   end
 
var  
   mFunkcja : TFunkcja; 
   x1, x2 : Integer
   zn : Char
BEGIN 
  writeln('Podaj dwie liczby'); 
  readln(x1,x2); 
  writeln('wybierz dzialanie: +dodawanie, -odejmownie,   
  * mnozenie,/ dzielenie'); 
  repeat  
    readln(zn); 
  until zn in ['+', '-', '*', '/']; 
  case zn of 
    '+' : mFunkcja := @dodaj; 
    '-' : mFunkcja := @odejmij; 
    '*' : mFunkcja := @pomnoz; 
    '/' : mFunkcja := @podziel; 
  end; 
  writeln('wynik wynosi= ', mfunkcja(x1, x2)); 
  readln; 
END. 

 

5.10.

 

Procedury kierujące działaniem programu

 

Wśród  procedur standardowych  modułu 

System oraz  SysUtils  znajdują  się 

procedury kierujące działaniem programu, mianowicie:

 

 

Exit – procedura ta słuŜy do przerwania bieŜącego podprogramu  (pro-
cedury lub funkcji) i powrotu do programu głównego. Jeśli jest wywoła-
na z programu głównego, to kończy jego działanie. 

 

Halt (kod_wyjścia) – procedura ta powoduje natychmiastowe zakończe-
nie działania programu i przekazuje sterowanie do systemu operacyjne-
go. Procedura 

Halt moŜe być wywołana bez parametru lub z jednym pa-

rametrem typu 

Integer, którego wartość jest zwracana systemowi opera-

background image

93 

 

cyjnemu  jako  kod  wyjścia  z  programu.  UŜycie  wywołania  procedury 
Halt  w  postaci  Halt;  albo  Halt (0);  oznacza  normalne  (bez  błędu)  za-
kończenie programu. Procedura 

Halt znajduje zastosowanie przy wywo-

łaniach danego programu tekstowego z innego programu. 

 

RunError – procedura ta powoduje przerwanie wykonywania programu 
i  wygenerowanie  błędu  jego  wykonywania  o  podanym  w  nawiasie  ko-
dzie (argument typu 

Byte). Wywołanie RunError; oznacza, Ŝe kod błę-

du wynosi 0, natomiast 

RunError (kod_błędu); zwraca kod_błędu

 

Sleep 

 procedura ta jest wywoływana z jednym parametrem typu 

Car-

dinal. Powoduje ona zatrzymanie programu na liczbę milisekund równą 
wartości podanego parametru. 

 

Abort 

  ta  bezparametrowa  procedura  generuje  tzw.  cichy  błąd,  który 

nie  jest  wyświetlany  w  postaci  komunikatu.  Procedura 

Abort  kończy 

działanie programu bez informacji o błędzie. 

 

Przykłady: 

program Pr_exit; 

var i : byte; 

 procedure zewnetrzna; 

   procedure wewnetrzna; 

    begin  

     writeln('To napisała procedura 

wewn

ę

trzna'); 

      exit; 

     writeln('a tego nigdy nie zo-

baczymy'); 

    end; 

 

begin  

 writeln('To napisała procedura ze-

wn

ę

trzna'); 

 wewnetrzna;  

    //tu wyw. procedur

ę

 wewnetrzn

ą

  

 writeln('ten napis si

ę

 pojawi, 

kiedy wewn

ę

trzna przeka

Ŝ

e sterowa-

nie do zewn

ę

trznej'); 

    exit; 

 writeln('A ten napis ju

Ŝ

 si

ę

 nie 

pojawi'); 

program Pr_sleep_halt; 

Uses SysUtils;  

//W tym module jest procedura sleep  

var  

   s : string; 

   I : byte; 

 

BEGIN  

 s := 'Dziala opoznienie- (sleep)'; 

 for i := 1 to length(s) do 

   begin  

     write(s[i]); 

     sleep(400); 

   end; 

    // readln; 

 

 

  writeln ('a teraz zadziała proce-

dura Halt, za 5 sekund'); 

 sleep(5000); 

 Halt; 

Writeln('Tego napisu nie zobaczy-

my')  

 END. 

background image

94 

 

 end; 

 

BEGIN      // program główny 

 writeln('wywołujemy procedur

ę

 ze-

wn

ę

trzn

ą

'); 

 zewnetrzna; 

 readln; 

 exit; 

 writeln('ten napis te

Ŝ

 si

ę

 nie po-

jawi'); 

 readln; 

END. 

 

 

5.11.

 

Kryteria stosowania procedur i funkcji 

Podczas podejmowania decyzji o wyborze procedury czy funkcji do zapro-

gramowania podprogramu, warto kierować się następującymi wskazówkami: 

 

jeśli podprogram ma zwracać więcej niŜ jedną wartość bądź modyfiko-
wać wartości parametrów aktualnych, naleŜy uŜyć procedury, 

 

jeśli  podprogram  ma  zwracać  dokładnie  jedną  wartość,  najlepiej  uŜyć 
funkcji, 

 

bardzo często procedurę moŜna zastąpić równowaŜną funkcją i odwrot-
nie, wtedy naleŜy wybrać ten sposób, który mamy lepiej opanowany, 

 

zmienne  mające  w  podprogramie  charakter  roboczy  naleŜy  deklarować 
jako zmienne  lokalne, 

 

bardziej  bezpieczne  i  zalecane  jest  przekazywanie  parametrów  aktual-
nych do podprogramu przez wartość, gdyŜ są one w ten sposób chronio-
ne  przed  niepotrzebnymi  zmianami  (wszelkie  zmiany,  jeŜeli  będą  w 
podprogramie, zostaną dokonane na kopiach parametrów aktualnych), 

 

jeśli  parametr  słuŜy  do  komunikowania  otoczeniu  efektów  wykonania 
podprogramu, to musi być przekazywany przez zmienną, 

 

jeśli  zachodzi  konieczność  przekazania  duŜej  struktury  danych  do  pod-
programu, to naleŜy unikać przekazywania jej przez wartość. Pozwoli to 
na  lepsze  gospodarowanie  pamięcią,  poniewaŜ  dla  parametrów  przeka-
zywanych przez wartość jest tworzona ich kopia na stosie. 

 

 

background image

95 

 

5.12.

 

Moduły 

Funkcje  i  procedury,  napisane  przez  uŜytkownika,  mogą  być  połączone 

w jeden 

moduł, z którego moŜna korzystać w wielu aplikacjach. 

Reguły tworzenia modułów są następujące: 

 

nazwa pliku, w którym znajduje się moduł, powinna być taka sama, jak 
nazwa modułu, 

 

moduł  składa  się  z  części  opisowej,  implementacyjnej  i  inicjalizującej; 
część inicjalizująca musi kończyć się słowem kluczowym 

end i kropką, 

 

stosowanie  modułów  powoduje,  Ŝe  właściwe  programy  są  znacznie 
krótsze, bardziej przejrzyste i czytelne, 

 

dzięki  temu,  Ŝe  moduły  przechowywane  są  w  postaci  skompilowanej, 
w trakcie kompilacji programu definicje zawarte w modułach są do pro-
gramu wynikowego dołączane, a zatem kompilacja przebiega szybciej. 
 

Fragment modułu, umieszczony pomiędzy słowami

 interface i implemen-

tation, jest tzw. częścią publiczną, a  pomiędzy słowami implementation oraz 
end
 jest tzw. częścią prywatną modułu. 

W  programach,  w  których  zadeklarowano  chęć  korzystania  z  modułu,  są 

dostępne tylko definicje umieszczone w części publicznej, tzn.:

 

 

Ŝaden  typ,  zmienna  lub  stała  umieszczona  w  części  prywatnej  nie 
jest dost
ępna w programie, 

 

definicje funkcji i procedur, niezaleŜnie od tego czy mają być dostępne 
w programie czy nie, muszą być umieszczone w części prywatnej, 

 

procedura  lub  funkcja  staje  się  publiczna,  gdy  jej

  nagłówek  zostanie 

umieszczony w części publicznej modułu. 

 

Liczba modułów, z których moŜe składać się program, jest praktycznie nie-

ograniczona. Jeśli jeden moduł korzysta z innego, to istotna jest kolejność nazw 
na liście deklarowanych modułów. Moduł wykorzystywany powinien znajdować 
się przed modułem wykorzystującym go. 

Nazwy występujące w programie są nadrzędne w stosunku do tych wystę-

pujących w modułach. Przesłonięte identyfikatory z modułu są dostępne w pro-
gramie, jeśli są poprzedzone nazwą modułu i kropką. 

Dołączanie modułu w programie odbywa się za

 pomocą słowa kluczowego 

uses według szablonu: 

program  z_wlasnym_modulem; 
uses  
  

SysUtils,  System,  Unit1 in 'Unit1.pas'; 

background image

96 

 

 

Unit  Unit1 
  Interface 
    // część publiczna 

 

  Implementation 
    // część prywatna 
End

 
Przykład. Definiowanie własnego modułu. 

 

Program  przyklad_modulu; 
uses SysUtils, modul1
var a : Tab; 
BEGIN 
  gener(A); 
  wypisz(A); 
  readln; 
END. 
 
Unit modul1; 
 
 Interface 
 
  const  n = 2; 
  type  Tab = array [1..N,1..N] of Integer
  procedure  gener(var T : Tab); 
  procedure  wypisz(T : Tab); 
 
 Implementation 
 
  procedure  gener(var T : Tab); 
  var  i, j : Byte
   Begin    
    for i:=1 to N do 
     for j:=1 to N do T[i,j] := random(10); 
   End; 
  procedure
 wypisz(T : Tab); 
  var  i, j : Byte
   Begin 
    for i := 1 to N do 
     for j := 1 to N do 
       if  j < N then write(T[i,j]:4,' ')  
       else writeln(T[i,j]:4) 
   End; 
End. // Unit … End.
 

 

background image

97 

 

5.13.

 

Zadania

 

Zad. 5.1. Napisać funkcję zamiany liczby dziesiętnej na szesnastkową. 

Zad. 5.2. Napisać funkcję zamiany liczby szesnastkowej na dziesiętną. 

Zad. 5.3.  Napisać  funkcję  obliczania  przybliŜonej  wartości  e

x

  według  sumy: 

!

...

!

2

!

1

1

e

2

n

x

x

x

n

x

+

+

+

+

,       x-dowolne. 

Zad. 5.4. Napisać funkcję obliczania przybliŜonej wartości cos(x) według sumy:

)!

2

(

)

1

...(

!

6

!

4

!

2

1

cos

2

6

4

2

n

x

x

x

x

x

n

n

+

+

=

,      x-dowolne. 

Zad. 5.5. Napisać program i rekurencyjną funkcję odwracania ciągu liter. Wśród 

parametrów  formalnych  funkcji  powinny  wystąpić:  napis  typu 

string 

oraz indeks typu 

Byte

Zad. 5.6.  Napisać  program  i  rekurencyjną  funkcję  obliczania  największego 

wspólnego dzielnika NWD dwóch liczb. 

Zad. 5.7. Napisać program obliczania największego wspólnego dzielnika n liczb. 

Zad. 5.8.  Napisać  program  i  funkcję,  która  zwróci  tablicę  n-elementową  typu 

Integer  (generowanie  danych  losowych).  Argumentem  wejściowym 
jest rozmiar tablicy, czyli n.

 

Zad. 5.9.  Napisać  funkcję,  która  zwróci  element  o  maksymalnej  wartości  bez-

względnej dla tablicy n-elementowej. 

Zad. 5.10. Napisać program i funkcję, która wyznaczy element najbardziej zbli-

Ŝ

ony do średniej arytmetycznej elementów tablicy n-elementowej typu 

Double.  

Zad. 5.11. Napisać funkcję Dwumian(nk : 

Cardinal) : wektor, gdzie  type wek-

tor  = 

array  of  Cardinal,  która  zwróci  wektor  czynników  powstałych 

po pełnym skróceniu ułamka dla współczynnika dwumianowego 

k

k

n

n

n

k

n

+

=





K

K

2

1

)

1

(

)

1

(

. PoniewaŜ  





k

n

 zawsze przyjmuje  warto-

ś

ci  całkowite,  więc  takie  skrócenie  jest  moŜliwe.  Na  przykład,  dla 

12

=

n

 i 

5

=

k

, po skróceniu ułamka, otrzymamy iloczyn: 

12

11

3

2

Uwaga: zadanie ma wiele poprawnych rozwiązań. 

 

 

background image

98 

 

6.

 

Operacje na plikach  

 

Plik  jest  logicznym  modelem  fizycznego  zbioru  danych.  Fizyczne  zbiory 

danych  mogą  być  wprowadzane  i  wyprowadzane  przez  urządzenia  zewnętrzne 
komputera takie jak: klawiatura, ekran, drukarka, dyski stałe, optyczne itp. Do-
stęp  do  poszczególnych  elementów  pliku  odbywa  się  sekwencyjnie,  tzn.  w  da-
nym  momencie  dostępny  jest  co  najwyŜej  jeden  element  pliku.  Następny  ele-
ment moŜe być dostępny dopiero po wykonaniu jakiejś operacji na poprzednim 
elemencie. Kolejność elementów w pliku zaleŜy od kolejności, w jakiej zostały 
do niego zapisane. Liczba elementów pliku jest ograniczona pojemnością dysku.  

Plik  jest  ciągiem  elementów  tego  samego  typu.  Liczba  elementów  pliku 

zaleŜy  od  programu  go  zapisującego.  Od  tablicy  plik  róŜni  się  metodą  dostępu 
do  poszczególnych  elementów  oraz  tym,  Ŝe  moŜe  istnieć  w  pamięci  dyskowej 
komputera po zakończeniu działania programu (i po wyłączeniu zasilania). 

Aby  w  programie  było  moŜliwe  odczytywanie  danych  z  pliku  lub  zapisy-

wanie  danych  do  pliku,  konieczne  jest  zdefiniowanie  zmiennej  plikowej  odpo-
wiedniego typu i skojarzenie jej (przywiązanie) do konkretnego pliku na dysku. 

Najlepiej  najpierw  zdefiniować  typ  plikowy,  a  potem  zmienną  plikową. 

Zmienna plikowa (zmienna wskazująca, wskaźnik połoŜenia) zawsze pokazuje 
na jakieś miejsce w pliku. Zmienna plikowa zezwala na dostęp do kaŜdego ele-
mentu pliku i pośredniczy we wszystkich operacjach na nim wykonywanych. 
 

6.1.

 

Rodzaje plików

 

MoŜliwe są trzy sposoby definicji typu plikowego: 

1)

 

type  nazwa_typu_plikowego = file of  typ_elementów_w_pliku

 
Ten typ plikowy oznacza 

plik zdefiniowany, poniewaŜ w sposób jawny podany 

jest  typ  elementów  w  pliku.  Nazwa  typ_elementów_w_pliku  moŜe  być  dowol-
nym  typem  prostym,  porządkowym,  strukturalnym,  wskaźnikowym  lub  zdefi-
niowanym  przez  uŜytkownika  (np.  rekordem).  Plik  zdefiniowany  jest  plikiem 
binarnym,  tzn.  niezrozumiałym  (nieczytelnym)  po  otwarciu  go  w  edytorze  tek-
stu. Przykłady deklaracji zmiennych plikowych typu zdefiniowanego: 
type dane = file of Double
var moj_plik : dane; 
 
type MaleLiczby  = file of Smallint
var plik_temp : MaleLiczby; 

background image

99 

 

2)

 

type  nazwa_typu_plikowego = file

 
PowyŜsza  definicja  oznacza,  Ŝe  typ  elementów  w  pliku  jest  niezdefiniowany. 
Pliki  niezdefiniowane  stosuje  się  w  celu  dostępu  do  fizycznych  zbiorów  dys-
kowych zgodnie z ich wewnętrznym formatem. Przykład:  

type plik = file

 
3)

 

type  nazwa_typu_plikowego = TextFile;   (albo Text

 
Lazarus FPC istnieje predefiniowany typ określający 

plik tekstowy. Elemen-

tami pliku tekstowego są wiersze składające się ze znaków. Znaki te mogą two-
rzyć liczby, wyrazy, tabele danych itp. KaŜdy wiersz zakończony jest parą zna-
ków CR i LF (ang. 

Carriage Return i Line Feed), których kody ASCII wynoszą 

13  i  10  w  systemie  dziesiętnym  oraz  D  i  A  w  systemie  szesnastkowym. 
Przykład deklaracji pliku tekstowego:   

var  plik_1  :  TextFile

6.2.

 

Etapy przetwarzania pliku

 

Na

 przetwarzanie pliku dowolnego rodzaju składają się następujące czynności: 

1.

 

Deklaracja zmiennej plikowej.  

2.

 

Skojarzenie zmiennej plikowej z fizycznym zbiorem danych.  

3.

 

Otwarcie pliku. 

4.

 

Wykonanie operacji na pliku (odczyt danych lub zapis). 

5.

 

Zamknięcie pliku. 

 
1. Przykłady deklaracji zmiennych plikowych: 

var  plik1 : file of Integer;  // plik zdefiniowany 
       
plik2 : file of Byte;       // plik zdefiniowany 
       plik3 : 

TextFile;           // plik tekstowy 

       plik4 : 

file;                   // plik niezdefiniowany 

 

type TZesp = record 
                         re, im : 

Extended

                      end
var plik5 : file of TZesp;     // plik zdefiniowany 

 
2. Do skojarzenia zmiennej plikowej z fizycznym zbiorem danych słuŜy proce-
dura standardowa  

AssignFile (zmienna_plikowałańcuch_znakowy); 

Przykłady kojarzenia zmiennej plikowej z plikiem na dysku: 

AssignFile (plik1, ’wyniki.bin’);      
AssignFile (plik2, ’c:\Dane\dane’);   // plik bez rozszerzenia 
AssignFile (plik3, ’osoby.txt’); 

background image

100 

 

3.    W  zaleŜności  od  kierunku  przesyłania  danych,  stosowane  są  trzy  sposoby 
otwierania lub tworzenia pliku: 
a) otwarcie istniejącego pliku do odczytu 

 zmienna plikowa pokazuje na pierw-

szy element w pliku. Przykłady: 

Reset (plik1);  Reset (plik2); 

 
b)  otwarcie  istniejącego  pliku  tekstowego  w  celu  dopisywania  tekstu  na  końcu 
pliku. Zmienna plikowa pokazuje na miejsce za ostatnim znakiem  w pliku tek-
stowym. Przykłady: 

Append (plik1);  Append (plik2);  Procedurę Append moŜna 

stosować tylko do plików tekstowych. 
 
c) utworzenie nowego pliku, w którym będą zapisywane dane. JeŜeli istniał plik 
o nazwie takiej samej, jak ta, którą chcemy uŜyć, to zostanie zniszczony. Przy-
kłady: 

Rewrite (plik1),  Rewrite (plik4,  10);  Drugie  wywołanie  jest  stosowane  

tylko do plików niezdefiniowanych, pierwsze 

 do wszystkich. Liczba 10 ozna-

cza, Ŝe kaŜdy zapis do pliku niezdefiniowanego będzie składał się z 10 bajtów. 
Zapis 

Rewrite (plik4) oznacza, Ŝe domyślny rozmiar zapisu wynosi 128 bajtów. 

 
4.  Przetwarzanie  pliku  oznacza  odczytywanie  danych  znajdujących  się  w  pliku 
albo zapisywanie danych do niego. 

 
Zapisywanie danych do plików tekstowych odbywa się za pomocą procedur 

Write i Writeln. Przykłady: 
Write (plik3, zmienna);    Write (plik3, zmienna1, zmienna2);   
Writeln (plik3, a, ’+’, b, ’=’, a+b); 

 
Zapisywanie danych do plików zdefiniowanych odbywa się za pomocą pro-

cedury 

Write. Przykłady:  Write (plik1, liczba);  Write (plik2, a, b, c); 

W tym przypadku zmienna liczba musi być typu 

Integer; natomiast zmienne a, b 

i c muszą być typu 

Byte

 
Zapisywanie  danych  do  plików  niezdefiniowanych  odbywa  się  za  pomocą 

procedury 

BlockWrite. Przykłady: 

BlockWrite (plik4, buforlicznik); Zmienna bufor jest zmienną dowolnego typu. 
Zmienna licznik jest typu 

Integer i określa liczbę zapisów do pliku niezdefinio-

wanego, które chcemy wykonać. Bajty do zapisu są pobierane po kolei z bufora. 
Liczbę  bajtów  dla  jednego  zapisu  określa  się  wcześniej  w  procedurze 

Rewrite

JeŜeli  rzeczywista  liczba  wykonanych  zapisów  na  dysk  jest  róŜna  od  wartości 
licznika,  to  wystąpi  błąd.  Jeśli  w  wywołaniu  funkcji 

BlockWrite  uŜyjemy  po-

mocniczej zmiennej faktyczny_licznik w postaci: 

BlockWrite (plik4, buforlicz-

nik,  faktyczny_licznik);  to  błąd  nie  wystąpi,  a  liczbę  udanych  zapisów  będzie 
moŜna odczytać w zmiennej  faktyczny_licznik

background image

101 

 

Odczyt danych z plików zdefiniowanych odbywa się za pomocą procedury 

ReadPrzykłady: Read (plik1, liczba);  Read (plik2, abc);  
W tym przypadku zmienna liczba musi być typu 

Integer; natomiast zmienne ab 

c muszą być typu 

Byte

 
Odczyt danych z plików niezdefiniowanych odbywa się za pomocą proce-

dury 

BlockReadPrzykłady: 

BlockRead (plik4, buforlicznik);       albo 
BlockRead (plik4, buforlicznikfaktyczny_licznik); 

Znaczenie  zmiennych  bufor,  licznik  i  faktyczny_licznik  jest  takie  samo,  jak  dla 
procedury 

BlockWrite

 
Odczyt danych z plików tekstowych odbywa się za pomocą procedur 

Read 

Readln. Przykłady: Read (plik3, znak); zmienna znak moŜe być typu np. Char

Readln (plik3, ab); zmienne a i b mogą być typu np. Integer i Extended

 

5. Po zakończeniu przetwarzania pliku naleŜy go zamknąć procedurą CloseFile
Przykłady: 

CloseFile (plik1);  CloseFile (plik2); 

 

 
W przetwarzaniu plików tekstowych wykorzystywane są następujące funk-

cje standardowe, zwracające 

True lub False

Eof (zmienna_plikowa

 funkcja ta zwraca wartość 

True, jeŜeli zmienna_ 

plikowa pokazuje na miejsce za ostatnim znakiem w  pliku lub gdy plik nie za-
wiera Ŝadnych znaków. 

EoLn (zmienna_plikowa

  funkcja  ta  zwraca  wartość 

True,  jeŜeli  zmien-

na_plikowa  pokazuje  na  znak  końca  wiersza  (CR)  lub  gdy 

Eof  (zmien-

na_plikowa) = 

True

SeekEof (zmienna_plikowa

  funkcja  ta  zwraca  wartość 

True,  jeŜeli 

zmienna_ plikowa pokazuje na miejsce za ostatnim znakiem w pliku lub gdy plik 
nie zawiera Ŝadnych znaków, lub gdy jedynymi znakami do końca pliku są znaki 
odstępu (spacje), tabulacji lub CR i LF

SeekEoLn (zmienna_plikowa

  funkcja  ta  zwraca  wartość 

True,  jeŜeli 

zmienna_plikowa  pokazuje  na  znak  końca  wiersza  (CR)  lub  gdy 

Eof  (zmien-

na_plikowa) = 

True, lub gdy jedynymi znakami do końca wiersza są znaki od-

stępu (spacje), tabulacji lub CR i LF
 

 

background image

102 

 

6.3.

 

Przykładowe programy działające na plikach 

 
Przykład 1. Zapis liczb rzeczywistych do pliku zdefiniowanego. 
 

program Pliki1; 
var 
    f : file of Real; 
    x : Real
BEGIN 
  AssignFile(f, 'plik.bin'); 
  Rewrite(f);
 
  x := 3.1415926; 
  write(f,x); 
  x := -1.8; 
  write(f,x); 
  write(f,x,x); // writeln(f,x,x); <-- niedozwolone 
  CloseFile(f); 
  readln; 
END. 

 

Przykład 2. Odczyt liczb rzeczywistych z pliku zdefiniowanego z przykładu 1. 
 

program Pliki2; 
var 
    f : file of Real; 
    x : Real
BEGIN 
  AssignFile(f, 'plik.bin'); 
  Reset(f); 
  while not EoF(f) do 
  begin  
    read(f, x); 
    writeln(x); 
  end; 
  CloseFile(f); 
  readln; 
END. 

 

Przykład 3. Zapis dwóch liczb rzeczywistych i całkowitej do pliku tekstowego. 
 

program Pliki3; 
var 
    f : TextFile;
 
    x : Real

background image

103 

 

 
BEGIN
 
  AssignFile(f, 'plik.txt'); 
  Rewrite(f); 
  x := 3.14; 
  writeln(f, x, ' ', -x,     ' ', 2+2); 
  writeln(f, x, ' ', -x:8:3, ' ',   5); 
  CloseFile(f); 
  readln; 
END. 

 

Przykład 4. Odczyt dwóch liczb rzeczywistych i całkowitej z pliku tekstowego. 
 

program Pliki4; //odczyt danych z pliku z przykładu 3. 
var  
    f : TextFile;
 
 x, y : Real
    i : Integer
BEGIN 
  AssignFile(f, 'plik.txt');   
  Reset(f); 
  while not EoF(f) do 
    begin  
      readln(f,x,y,i); 
      writeln('x=', x, ' y=', y, ' i=', i); 
    end; 
  CloseFile(f); 
 
 readln;  
END. 

 

Przykład 5. Zapis liczb całkowitych z tablicy do pliku tekstowego i ich odczyt. 
 

program wewy1; 
const  N = 5; 
var 
 we, wy : TextFile;
 
     zn : Char
   A, B : array [1..N] of Byte
      i : Byte
BEGIN 
  for i := 1 to N do A[i] := 10*i; 
  AssignFile(we, 'Tplik.txt'); 
  Rewrite(we); 
  for i := 1 to N do Writeln(we, A[i]); 
  CloseFile(we); 

background image

104 

 

  AssignFile(wy, 'Tplik.txt'); 
  Reset(wy); 
  i := 1; 
  while (not EoF(wy)) and (i<=N) do 
    begin  
      readln(wy, B[i]); 
      write(B[i]); 
      i := i+1; 
    end; 
  CloseFile(wy); 
  readln;
 
END. 

 

Przykład 6. Odczyt łańcuchów znakowych z pliku tekstowego. 
 

program wewy2; 
var  
   we1 : TextFile;
 
 napis : string
BEGIN 
  AssignFile(we1, 'wewy2.lpr'); 
  Reset(we1); 
  while not EoF(we1) do 
   begin  
     readln(we1, napis); 
     if length(napis)<>0 then  
      if napis[1] in ['A'..'Z'] then writeln(napis) 
 
      // wypisuje na ekranie tylko te wiersze, 
       // które zaczynaj

ą

 si

ę

 od wielkiej litery 

   end; 
  CloseFile(we1); 
  readln; 
END. 
 

6.4.

 

Zadania 

 

Zad. 6.1. W pliku tekstowym liczby.txt są zapisane liczby rzeczywiste – po dwie 

w kaŜdym wierszu, oddzielone białymi znakami. Program ma czytać te pa-
ry liczb. Jeśli ich iloczyn będzie większy od zera, to program ma zapisać go 
do  pliku  tekstowego 

dod.txt.  Jeśli  ich  iloczyn  będzie  mniejszy  od  zera,  to 

program ma go zapisać do pliku 

ujem.txt

Zad. 6.2. W pliku tekstowym 

we1.txt znajduje się kilka wierszy tekstu. Napisać 

program zawierający funkcję Parzystosc_E

(s : string), która sprawdzi, czy 

w danym wierszu jest parzysta liczba znaków ’e’ i ’E’. Funkcja ma zwracać 

background image

105 

 

wartość 

True,  jeŜeli  liczba  znaków  ’e’  i  ’E’  jest  parzysta  (zero  teŜ  jest 

parzyste)  oraz  ma  zwracać  wartość 

False  w  przeciwnym  wypadku. 

Argumentem  wejściowym  funkcji  jest  wiersz  pliku.  Wynik  działania 
funkcji przedstawić na ekranie. 

Zad. 6.3. Program czyta wiersze pliku tekstowego 

we6.txt. Za kaŜdym razem, po 

napotkaniu  wiersza,  którego  pierwszym  znakiem  jest  'A'..'Z',  rozpoczyna 
łączenie  (sklejanie)  kolejnych  wierszy,  rozdzielając  je  spacją.  Sklejony 
wiersz jest zapisywany do pliku 

wy6.txt, przy czym na początku, w nawia-

sach, jest wpisywana liczba jego znaków.  

Zad. 6.4. Program ma czytać plik tekstowy 

zad4.lpr 

 wiersz po wierszu 

 i jeśli 

w  wierszu  znajdzie  ciąg  dwóch  ukośników  '//',  to  zapisze  dalszy  ciąg  tego 
wiersza do pliku tekstowego 

komentarze.txt oraz wypisze na ekranie. 

Zad. 6.5.  Napisać  program  Dekomentator,  który  wejściowy  plik  tekstowy, 

zawierający  kod  źródłowy  programu  w  Lazarus  FPC,  przetworzy  tak,  Ŝe 
usunie  z  niego  wszystkie  moŜliwe  komentarze  (jedno  i  wielo-liniowe), 
w tym takŜe zagnieŜdŜone. Uwaga na dyrektywy kompilatora! 

Zad. 6.6.  W  kolejnych  wierszach  pliku  tekstowego 

we5.txt  znajduje  się  albo 

liczba naturalna, albo jakiś napis. Wiersz z liczbą naturalną tym róŜni się od 
napisu,  Ŝe  występują  w  nim  wyłącznie  cyfry  '0'..'9'  oraz  znaki  ’+’,  ’

’. 

Napisać  program,  który  odczyta  kolejne  wiersze  wspomnianego  pliku 
i  zapisze  do  pliku  tekstowego 

wy8.txt  wyłącznie  wiersze  zawierające 

napisy.  Trzeba  zaprogramować  funkcję 

JestLiczba  zwracającą  dla  danego 

wiersza wartość 

True, jeśli zawiera on liczbę i False, jeśli zawiera on napis.  

Zad. 6.7.  Napisać  program,  który  wczyta  plik  tekstowy 

we7.txt  i  sprawdzi,  ile 

zdań jest w tym pliku. Zdanie zaczyna się wielką literą i kończy się kropką, 
pytajnikiem lub wykrzyknikiem. 

 

 

background image

106 

 

7.

 

Wskaźniki 

 
Zmienne typów prostych i strukturalnych istnieją przez cały czas wykony-

wania tej części programu, w której zostały zadeklarowane. Są to tzw. 

zmienne 

statyczne.  

Zmienne dynamiczne natomiast reprezentują obiekty, dla których pamięć 

jest  przydzielana  i  zwalniana  na  Ŝądanie,  w  trakcie  pracy  programu.  Zmienna 
dynamiczna nie posiada własnego identyfikatora, a odwołanie do niej następuje 
za pomocą zmiennej typu wskaźnikowego, w której pamiętany jest adres w pa-
mięci, gdzie ta zmienna dynamiczna się znajduje. 

 

7.1.

 

Definicja zmiennej wskaźnikowej 

Definicja zmiennej wskaźnikowej (w skrócie 

 wskaźnika) określa, jak in-

terpretować  zawartość  pamięci  pod  adresem  pokazywanym  przez  wskaźnik. 
Na  przykład  definicja  wsk  :  ^ 

Byte;  oznacza,  Ŝe  obszar  pamięci,  pokazywany 

przez wskaźnik wsk, zawiera liczbę typu 

Byte

Przykłady deklaracji wskaźników: 

var  
   wsk_int :  ^ 

Integer;    {wskaźnik na zmienną typu Integer

   wsk_char : ^ 

Char;      {wskaźnik na zmienną typu Char

   wsk_real : ^ 

Single;     {wskaźnik na zmienną typu Single}  

type  
   tab = 

array [1..10] of Integer;    {definicja typu tablicowego} 

   wsk = ^ tab;      {definicja typu będącego wskaźnikiem do tablicy} 
var  
  wsk_tab :  ^ tab;  {definicja zmiennej wskazującej na tablicę} 
  // wsk_tab : wsk;     

 to samo, co     wsk_tab :  ^ tab;      

tab_wsk : 

array [1..10] of  ^ Integer;   {tablica zawierająca 10 wskaźników  

do liczb całkowitych typu 

Integer

var  p : Pointer;   {wskaźnik na zmienną dowolnego typu} 

Zmienne  typu 

Pointer  nie  pokazują  na  dane  Ŝadnego  konkretnego  typu. 

Zawartość  zmiennej  typu 

Pointer  moŜna  podstawić  do  zmiennej  dowolnego 

typu wskaźnikowego i na odwrót. 
 
Uwagi dotyczące wskaźników: 

 

deklaracja zmiennej wskaźnikowej nie jest jednoznaczna z utworzeniem 
zmiennej wskazywanej, 

background image

107 

 

 

kaŜda zmienna wskaźnikowa musi być przed uŜyciem zainicjalizowana, 

 

nazwa zmiennej odwołuje się do obszaru pamięci o określonym adresie, 

 

adres pamięci, pod którym znajduje się zmienna, moŜna uzyskać za po-
mocą operatora @ , np. @ zmienna

 

ten  sam  wskaźnik  moŜe  odwoływać  się  do  róŜnych  miejsc  w  pamięci, 
ale nie równocześnie, 

 

wskaźnik  słuŜący  do  pokazywania  na  obiekty  jednego  typu  nie  moŜe 
(zazwyczaj) pokazywać na obiekty innego typu, 

 

za pomocą wskaźnika uzyskujemy dostęp do pewnego obszaru pamięci 
i moŜemy modyfikować jego zawartość, 

 

wskaźniki mogą być elementami rekordów, 

 

dozwolone jest uŜywanie wskaźników do rekordów. 

 

Zmiennej wskaźnikowej moŜna nadać wartość początkową na kilka sposo-

bów, mianowicie poprzez: 

 

przypisanie standardowej wartości stałej 

nil

 

przypisanie adresu zmiennej dynamicznej utworzonej za pomocą proce-
dury 

New lub GetMem, 

 

przypisanie adresu istniejącej  zmiennej,  której  typ jest  taki,  na jaki  po-
kazuje wskaźnik,  

 

przypisanie wartości innej zainicjalizowanej zmiennej wskaźnikowej. 

 

7.2.

 

Procedury dynamicznego rezerwowania i zwalniania pamięci 

Wskaźnik pokazuje na pewien obszar w pamięci i w związku z tym: 

 

moŜe to być obszar przechowujący zmienną, a więc juŜ zarezerwowany, 

 

moŜna  zarezerwować  (zaalokować)  nowy  obszar  dla  wskaźnika  za  po-
mocą procedury 

New

 

jeśli  obszar  pamięci  jest  juŜ  niepotrzebny,  to  naleŜy  go  zwolnić  za  po-
mocą procedury

 Dispose, 

 

obiekty utworzone za pomocą operatora 

New istnieją dopóty, dopóki nie 

zostaną zlikwidowane procedurą 

Dispose

 

do obiektów utworzonych za pomocą procedury 

New moŜna odwoływać 

się 

tylko za pomocą wskaźnika, 

 

obiekty tworzone w ten sposób są dynamiczne i nie są wstępnie inicjali-
zowane zerami. 

 

Standardowa stała 

nil oznacza, Ŝe zmienna wskaźnikowa na nic nie pokazu-

je; 

nil jest to tzw. adres „zero”, czyli Ŝaden konkretny adres. Przykład: 

var  wsk : ^ Integer;   . . .  wsk := nil;    // wsk pokazuje na adres zerowy 

background image

108 

 

 

Rys. 7.1.  Wskaźnik z adresem pustym (nil

 

Przypisanie wskaźnikowi wartości 

nil jest wykorzystywane do zaznaczenia, 

Ŝ

e wskaźnik nie pokazuje na nic konkretnego. Bez takiego przypisania wskaźnik 

pokazuje na jakiś losowy adres w pamięci. 

 
Powstanie zmiennej dynamicznej w : ^typ1, za pomocą procedury 

New, od-

bywa się w kilku etapach: 

 

utworzenie  nowej  zmiennej  dynamicznej  typu  typ1,  która  w  tym  mo-
mencie jest nieokreślona 

 nadawana jest jej automatycznie nazwa w^. 

 

utworzenie nowego wskaźnika w typu ^typ1,  który wskazuje na zmien-
ną typu typ1. 

 

nadanie  wartości  tego  wskaźnika  zmiennej,  dla  której  procedura 

New  

została  wywołana  (czyli  nadanie  zmiennej  dynamicznej  w

^  adresu  pa-

mięci przeznaczonego dla zmiennej typu typ1). 

 

zapisanie do  obszaru  pamięci,  na  który  wskazuje  wskaźnik  w,  konkret-
nej wartości jakiegoś wyraŜenia

 
var  w : ^typ1;  
New(w); 
w^ := wyraŜenie;  

 

Rys. 7.2. Wskaźnik W pokazujący obszar pamięci W^

 

 

Procedura 

GetMem róŜni się od procedury New tym,  Ŝe wielkość zarezer-

wowanego miejsca w pamięci określana jest dopiero w czasie działania progra-
mu i sami moŜemy zdecydować o rozmiarze potrzebnej pamięci, podając drugi 
parametr tej procedury, tzn. liczbę bajtów: 

 

wsk : ^typ
GetMem (wsk,  liczba_bajtów); 

 
Przykład uŜycia procedury  

GetMem: 

type   tab = array [1..20] of Integer
          wsk_tab = ^tab; 

background image

109 

 

var 
   wpom  : wsk_tab; 
 

… 

GetMem (wpom, 20*SizeOf (Integer)); {utworzenie zmiennej dynamicznej 
20 elementach typu 

Integer} 

wpom^ [  1] :=   5; { poprawne odwołanie do pierwszego elementu 
wpom^ [

2] := 10; { ąd zakresu zgłaszany przez kompilator 

wpom^ [25] := 

7; { błąd zakresu niezgłaszany przez kompilator (25>20) } 

 

Funkcja standardowa 

Addr (x) zwraca wskaźnik typu Pointer, zawierający 

adres miejsca, gdzie zapamiętane jest x, np.  w := 

Addr (x); 

 
Operator adresu @ słuŜy do odczytania adresu zmiennej, na przykład: 
var
   x : typ
       w : ^typ
…  
x
 := 12; 

 

    

:= @ x;    // wtedy  w^ = x = 12 

 
Przykład 1.  Wskaźnik na łańcuch znakowy. 
 

program wsk1; 
type
  TDane = string[60]; 
var  
  w1 : ^TDane; 
BEGIN 
  New(w1); 
  w1^ := 'Cos tam'; 
  Writeln(w1^); 
  w1^[2] := 'U'; //podmiana litery 'o' na 'U'  
  Writeln(w1^); 
  Dispose(w1); 
  readln; 
END. 
 

Przykład 2.  Dwa wskaźniki na ten sam łańcuch znakowy. 

 
program wsk2; 
type
  TDane = string[60]; 
var   w1, w2 : ^TDane; 
BEGIN  
  New(w1); 
  w1^ := 'Cos tam'; 

background image

110 

 

  w2  := w1; 
  w2^ := 'Cos innego tam jest'; 
  writeln(w1^); 
  writeln(w2^); 
  dispose(w2); 
  readln; 
END. 

 

Przykład 3.

 

UŜycie zmiennych wskaźnikowych i dynamicznych. 

 
program wsk3;
 
type  
  wsk_int = ^Integer
  typDanych = record  
                imie, nazwisko : string
              end; 
  wsk_rek = ^typDanych; 
var  
   x1, x2 : wsk_int; 
   r1 : wsk_rek; 
 
BEGIN 
 
  New(r1); // zmienna dynamiczna typu rekordowego 
  writeln('Podaj imi

ę

 i nazwisko: '); 

  readln(r1^.imie, r1^.nazwisko); 
  with r1^ do writeln(imie, nazwisko); 
  dispose(r1); 
  New(x1);  // zmienna dynamiczna typu integer  
  New(x2); 
  x1^ := 5; 
  x2^ := 10; 
  writeln('x1^=', x1^);  
  x2^ := x1^;   // przepisanie zawarto

ś

ci 

  x2  := x1;    // przepisanie adresu 
  writeln('x1^=', x1^); 
  writeln('x2^=', x2^); 
  dispose(x1); 
  // dispose(x2); // 
nie mo

Ŝ

na zwolni

ć

 dwa razy 

  readln; 
END. 

 
Zwalnianie  pamięci  przydzielonej  wskaźnikowi  w  sposób  dynamiczny  od-

bywa  się  za  pomocą  procedur 

Dispose  i  FreeMem.  Procedury  te  umoŜliwiają 

powtórne wykorzystanie obszarów pamięci oddawanych przez niepotrzebne juŜ 
zmienne dynamiczne. 

background image

111 

 

przydzielenie pamięci: 

 

 

zwolnienie pamięci: 

New (p);   

 

 

 

Dispose (p); 

GetMem (pliczba_bajtów); 

 

FreeMem (pliczba_bajtów); 

 

7.3.

 

Wskaźniki i tablice 

Wskaźniki są przydatne do pracy z tablicami. Zapis  w := @tab[3];  powo-

duje, Ŝe wskaźnik  w  ustawia się na elemencie o indeksie 3 tablicy  tab

Nazwa  tablicy  jest  równocześnie  adresem  jej  pierwszego  elementu,  zatem 

przypisanie    w  :=  @tab;   jest równowaŜne  przypisaniu    w  =  @tab[pocz],   jeśli 
pocz  jest pierwszym elementem tablicy  tab

 

Przykład 1. Tablica wskaźników do łańcuchów znakowych. 

 
program wsk4; 
type
   TDane = string[30]; 
const   MaxN = 5; 
var  A : array [0..MaxN-1] of ^TDane; // tablica wsk. 
     i : 0..MaxN; 

BEGIN                  // 

 tablica pustych wska

ź

ników 

  for i := 0 to MaxN-1 do A[i] := nil;  
  New(A[2]); 
  A[2]^ := 'NapisNr1';  //dodawanie danych do tablicy 
  New(A[4]); 
  A[4]^ := 'NapisNr2'; 
  for i := 0 to MaxN-1 do 
    if A[i] <> nil then     
      begin  
       writeln('nr: ', i, '  ', A[i]^);    
       dispose(A[i]); //usuwanie danych z tablicy 
       // A[i] := nil; 
      end; 
  readln; 
END

 
Przyklad 2. Wskaźnik do tablicy statycznej. 

 
program wsk5; 
const
 MaxN = 5; 
type  Tab = array [1..MaxN] of Byte
      wskTab = ^Tab; 
var   i : Byte;     
      B : Tab;   //tablica statyczna 

background image

112 

 

      : wskTab; 

BEGIN       // 

wypełnienie tablicy statycznej liczbami 

  for i := 1 to MaxN do B[i]:=i;  
  w := @B;         // ustawienie wska

ź

nika na tablic

ę

 

  writeln(w^[1]);  // writeln(B[1]); 
  inc(w);          // inkrementacja adresu 
  writeln(w^[2]);  // poza adres całej tablicy 
 
  w := @B[3]; // wska

ź

nik pokazuje na trzeci element 

  writeln(w^[1]);    
  readln;   
END

 
Przykład 3. 
Wskaźniki do tablicy dynamicznej. 

 
program wsk6; 
const
 N = 5; 
      type Tab = array [1..N] of Byte
      wskTab = ^Tab; 
var   i : Byte;  
      k : ^Byte
      w, x : wskTab; 
BEGIN  
  New(w);
 // utworzenie dynamicznej tablicy 
  for i := 1 to N do w^[i] := i;  // wpisanie danych 
  writeln('Pierwszy =', w^[1]); 
  writeln('Drugi    =', w^[2]); 
  writeln('N-ty     =', w^[N]); 
  x := w; 
  x^[N] := 115; 
  for i := 1 to N do write(x^[i]:5); 
  writeln; 
  k := @w; 
  writeln(k^); 
  k := @w[1];// pierwszy element tablicy 
  writeln(k^); 
  Dispose(w);//zwolnienie pami

ę

ci przydzielonej tablicy 

  readln;  
END

 

 

background image

113 

 

7.4.

 

Operacje arytmetyczne na wskaźnikach 

PoniewaŜ wskaźniki przechowują adresy, które są liczbami, to dopuszczal-

ne są niektóre operacje arytmetyczne na wskaźnikach: 

 

dodawanie  do 

  i 

odejmowanie  od  wskaźników  liczb  naturalnych,  za 

pomocą  procedur  inkrementacji 

Inc  i  dekrementacji  Dec,  powoduje  

przesuwanie wskaźników w kierunku wyŜszych lub niŜszych adresów.  



 

PoniewaŜ  wskaźnik  pokazuje  na  obiekt  pewnego  typu  i  znany 
jest rozmiar tego typu, więc wiadomo o ile bajtów trzeba prze-
sunąć wskaźnik. 



 

Operacje  zwiększania  i  zmniejszania  wskaźników  mają  istotne 
zastosowanie  w  stosunku  do  elementów  tablic.  JeŜeli  np. 
zmienna wskaźnikowa zw wskazuje na piąty element tablicy, to 
po 

Dec(zw) będzie wskazywać na czwarty element, niezaleŜnie 

od typu elementów tablicy. 



 

UŜywając procedur 

Inc i Dec trzeba uwaŜać, Ŝeby nie wyjść ze 

wskazaniem poza zakres tablicy. 



 

Zwiększenie  lub  zmniejszenie  zmiennej  wskaźnikowej  o  okre-
ś

lonym typie (ale nie 

Pointer) teŜ odbywa się za pomocą proce-

dur 

Inc i Dec. Na przykład: x : ^ Double;  Inc (x, 2)  spowoduje 

zwiększenie wskaźnika x o 2*8 = 16 bajtów, natomiast  k : ^ 

In-

teger;  Inc (k, 3) spowoduje zwiększenie k o 3*4 = 12 bajtów. 

 

odejmowanie dwóch wskaźników pokazujących na tę samą tablicę 

 w 

wyniku dostajemy liczbę elementów tablicy dzielących elementy poka-
zywane przez oba wskaźniki. Liczba ta moŜe być ujemna lub dodatnia. 

 

porównywanie  wskaźników 

  wskaźniki  moŜna  ze  sobą  porównywać 

za pomocą operatorów porównania:   =, 

<>, <, >, <=, >=, 



 

równość wskaźników oznacza, Ŝe pokazują one na ten sam obiekt, 



 

wskaźnik, który jest mniejszy, pokazuje na element o niŜszym ad-
resie (w tablicy – na element o niŜszym indeksie). 

JeŜeli  w  oraz  x  są  zmiennymi  wskaźnikowymi,  to  porównanie  adresów 

przechowywanych w tych wskaźnikach przeprowadza się tak: w < x, natomiast 
porównanie zawartości komórek pamięci odbywa się tak:  w^ < x^.  
 

 

background image

114 

 

8.

 

Struktury dynamiczne  

 

W wielu algorytmach pojawia się potrzeba wykorzystania struktur danych, 

umoŜliwiających  w  sposób  dynamiczny  wstawianie  i  pobieranie  danych.  Naj-
prostszymi taki strukturami są 

kolejkalista i stos

 

8.1.

 

Kolejka 

Kolejka to rodzaj listy jednokierunkowej, do której moŜna dopisać element 

tylko na końcu, a usunąć tylko element znajdujący się na początku. 

Kolejka realizuje strategię wstawiania/pobierania, opartą na zasadzie pierw-

szy  wszedł,  pierwszy  wyjdzie.  Kolejka  zachowuje  kolejność  wstawianych  ele-
mentów. 

Operacje, które wykonuje się na kolejce to: wstawianie elementu, pobranie 

elementu,  usunięcie  elementu,  usunięcie  całej  kolejki,  wyświetlenie  zawartości 
kolejki oraz sprawdzenie, czy kolejka jest pusta. 
 

 

Rys. 8.1. Schemat blokowy kolejki

 

8.2.

 

Lista jednokierunkowa 

Lista  jednokierunkowa  jest  zbiorem  elementów  zwanych  węzłami,  z  któ-

rych kaŜdy jest zwykle rekordem i składa się z dwóch części: pola danych i pola 
wskaźnikowego, wykorzystywanego jako łącznik z następnym elementem listy. 

 

type 
   wsk = ^ skladnik; 
   skladnik = 

record  

                        dane : typ
                        next  : wsk  
                     

end

 

 

background image

115 

 

Dostęp  do  listy  odbywa  się  poprzez  wskaźnik,  który  zawiera  adres  pierw-

szego  elementu  na  liście. Wskaźnik  ten  nazywany  jest  początkiem  bądź  korze-
niem listy. KaŜdy następny składnik listy jest dostępny poprzez składową zawie-
rającą adres w składniku poprzednim. 
 

 

Rys. 8.2. Schemat blokowy listy

 

 
Przykład tworzenia nowej listy:  

type 
  wsk = ^ skladnik; 
  skladnik = record  

          dane : typ
          next : wsk 
       end

 

procedure  tworz_liste; 
var  zn : Char;  
begin  
 pocz := nil
 repeat  
  New(wsk); 
  write('Podaj kolejny 
  element listy') 
  readln(wsk^.dane); 
  wsk^.next := pocz; 
  pocz := wsk; 
  write('Czy kontynuowa

ć

?'); 

  readln(zn) 
 until zn <> 't'; 
end;

 

 
NajwaŜniejsze operacje dotyczące wszystkich typów list to: 

 

dopisywanie nowego elementu: 



 

kiedy lista jest pusta, 



 

na jej początku, 



 

na jej końcu, 



 

w dowolnym miejscu wewnątrz listy. 

background image

116 

 

 

usuwanie elementu: 



 

znajdującego się na początku, 



 

znajdującego się na końcu, 



 

w dowolnym miejscu wewnątrz listy. 

 

wyświetlanie listy. 

 

Aby  moŜliwe  było  wykonanie  operacji  wstawiania  oraz  usuwania  nowych 

elementów, lista musi być posortowana według określonego klucza. Kluczem do 
sortowania  moŜe  być  np.  pole  nazwisko,  jeśli  mamy  do  czynienia  z  listą  osób. 
Nazwiska typu łańcuchowego moŜemy łatwo porównywać alfabetycznie. 
 
Przykład. Program tworzy listę dynamiczną, ale bez sortowania. Polem danych 
jest łańcuch 60-znakowy. Program zawiera procedury: dopisywania elementu na 
początku listy, wypisywania całej listy i usuwania listy z pamięci. 

 

program Lista1; 
type  
  TDane = string[60];    // ła

ń

cuch 60-znakowy 

              
  PElem = ^TElem; 
  TElem = record  
           Dane : TDane; // pole danych 
           Nast : PElem; // wska

ź

nik na nast

ę

pny elem. 

          end
 
  TLista = PElem;        // wska

ź

nik na pocz

ą

tek listy 

 
procedure Inicjalizuj(var Lista : TLista); 
begin  
  Lista := nil; 
end
 
procedure Dopisz(var Lista : TLista; D : TDane); 
var  w : PElem; 
begin  
  w := Lista; 
  New(Lista); 
  Lista^.Dane := D; 
  Lista^.Nast := w; 
end
 
procedure WypiszWszystko(Lista : TLista); 
var  w : PElem; 
 

background image

117 

 

begin  
  w := Lista; 
  while w <> nil do 
  begin 
    writeln(w^.Dane); 
    w := w^.Nast; 
  end; 
end
 
procedure UsunWszystko(var Lista : TLista); 
var  w : PElem; 
begin  
  while Lista <> nil do   
    begin  
      w := Lista; 
      Lista := Lista^.Nast; 
      Dispose(w); 
    end;   
end
 
var  Lst : TLista; 
BEGIN  
  Inicjalizuj(Lst); 
  Dopisz(Lst, 'pierwszy'); 
  Dopisz(Lst, 'drugi'); 
  Dopisz(Lst, 'trzeci'); 
  WypiszWszystko(Lst); 
  UsunWszystko(Lst); 
  readln; 
END

 

8.3.

 

Stos jako dynamiczna struktura danych 

Stos  jest  dynamiczną  strukturą  danych,  która  umoŜliwia  wstawianie  ele-

mentów  i  ich  pobieranie  tylko  z  początku,  zwanego  wierzchołkiem  stosu.  Stos 
realizuje strategię wstawiania/pobierania LIFO (Last In-First Out), czyli ostatni 
wszedł, pierwszy wyjdzie
. Stos odwraca kolejność wstawianych elementów. 

Operacje wykonywane na stosie to: 

 

wstawienie elementu na stos (ang. push),  

 

zdjęcie elementu ze stosu (ang. pop),  

 

sprawdzenie, czy stos jest pusty (ang. empty),  

 

odczytanie elementu na szczycie (ang. top),  

 

inicjalizacja stosu (ang. init),  

background image

118 

 

 

usunięcie całego stosu (ang. clear), 

 

wyświetlenie zawartości stosu (ang. display). 

 

Przykład. Program wykonuje operacje arytmetyczne na elementach stosu. Ele-
mentami stosu są tablice kwadratowe. Zaprogramowane operacje to: wkładanie 
tablicy  na  stos,  zdejmowania  tablicy  ze  stosu  oraz  dodawanie,  odejmowanie 
i mnoŜenie dwóch tablic znajdujących się na szczycie stosu. W programie głów-
nym zakodowano operacje: C := A+B,  C := A

B, C := A*(B+C). 

 

Program Stos; 
const
 NMax = 2; 
type  float = Double
      TArr  = array [0..NMax-1, 0..NMax-1] of float; 
 
      PElement = ^TElement; 
      TElement = record  
                  poprz : PElement; 
                      X : TArr; 
                 end; 
      TStos = PElement; {wska

ź

nik na wierzchołek stosu

 
procedure NaStos
(var St : TStos; _X : TArr); 
{_X - dane nowego elementu stosu
  var  w : PElement; 
  begin  
    New(w); 
    w^.X := _X; 
    w^.poprz := St; 
    St := w; 
  end
 
procedure ZeStosu(var St : TStos; var _X : TArr);  
{X - zawarto

ść

 likwidowanego elementu

  var  w : PElement; 
  begin 
    if (St=nil) then EXIT; {bł

ą

d!} 

    w := St; 
    _X := w^.X; 
    St := w^.poprz; 
    Dispose(w); 
  end
 
 
 

background image

119 

 

procedure Dodaj(var St : TStos); 
{Dodaje dwa szczytowe elementy stosu - zdejmuje pierw-
szy, a wynik umieszcza w wierzchołku stosu.

  var i, j : 0..NMax-1; 
         T : TArr; 
  begin 
    ZeStosu(St, T); 
    for i := 0 to NMax-1 do 
      for j := 0 to NMax-1 do 
        St^.X[i,j] := St^.X[i,j] + T[i,j]; 
  end; 
 
procedure Odejmij(var St : TStos);  
{Odejmuje najwy

Ŝ

szy od ni

Ŝ

szego  elementu stosu - zdej-

muje 1 element, a wynik umieszcza w wierzchołku stosu} 
  var i, j : 0..NMax-1;  
         T : TArr; 
  begin  
    ZeStosu(St, T); 
    for i := 0 to NMax-1 do 
      for j := 0 to NMax-1 do 
        St^.X[i,j] := St^.X[i,j] - T[i,j]; 
  end
 
procedure Mnoz(var St : TStos); 
{Mno

Ŝ

y elementy stosu i wynik umieszcza  

 w wierzchołku stosu
  var i, j, k : 0..NMax-1; 
       A {czynnik}, C {wynik} : TArr; 
  begin  
    ZeStosu(St, A); {drugi czynnik
    for i := 0 to NMax-1 do 
      for j := 0 to NMax-1 do  
       begin  
        C[i,j] := 0; 
        for k := 0 to NMax-1 do 
          C[i,j] := C[i,j] + St^.X[i,k]*A[k,j]; 
       end
    
    ZeStosu(St, A); {zdejmuje ze stosu drugi czynnik
    NaStos(St, C);  {wkłada iloczyn na stos
  end
 
procedure Wyswietl(Nagl : string; A : TArr); 
{wy

ś

wietla element wskazywany przez W,  

na pocz

ą

tku wypisuje nagłówek w postaci ła

ń

cucha} 

background image

120 

 

  var i, j : 0..NMax-1; 
  begin  
    writeln(Nagl); 
    for i := 0 to NMax-1 do 
     begin  
      for j := 0 to NMax-1 do 
        write(A[i,j]:6:1, ' '); 
      writeln; 
     end
    writeln; 
  end
 
 procedure InicjalizujDowolnie(var A : TArr); 
  var i, j : 0..NMax-1; 
  begin  
    for i := 0 to NMax-1 do 
      for j := 0 to NMax-1 do 
        A[i,j] := random(10)-5.0; 
  end
 
procedure Pause
  begin 
    write('Naci

ś

nij ENTER...':60); 

    readln;  
  end
 
procedure TestStNil
  begin 
   if St<>nil then writeln('Program zawiera bł

ę

dy!'); 

  end
 
var St : TStos; 
    A, B, C : TArr; 
 
BEGIN 
  writeln(#13#10,'Stos i odwrotna notacja polska':60); 
  St := nil; 
  Randomize; 
  InicjalizujDowolnie(A); 
  InicjalizujDowolnie(B); 
  Wyswietl('A:', A); 
  Wyswietl('B:', B); 
  NaStos(St, A); 
  NaStos(St, B); 
  Dodaj(St); 
  ZeStosu(St, C); 

background image

121 

 

  Wyswietl('C=A+B', C); 
     TestStNil; Pause; 
  NaStos(St, A); 
  NaStos(St, B); 
  Odejmij(St); 
  ZeStosu(St, C); 
  Wyswietl('C=A-B', C); 
     TestStNil; Pause; 
  NaStos(St, A); 
  NaStos(St, B); 
  Mnoz(St); 
  ZeStosu(St, C);  
  Wyswietl('C=A*B', C); 
     TestStNil; Pause; 
  NaStos(St, A); 
  NaStos(St, B); 
  NaStos(St, C); 
  Dodaj(St); 
  Mnoz(St); 
  ZeStosu(St,C); 
  Wyswietl('C=A*(B+C)', C); 
     TestStNil; Pause; 
END. 

 

8.4.

 

Zadania 

Zad. 8.1. Zadeklarować  tablicę  100  wskaźników  do  rekordów.  W  rekordzie 

umieścić imię i nazwisko typu 

string. Program ma czytać z klawiatury in-

deks komórki do wypełnienia. Dane wprowadzamy dopóty, dopóki poda-
ny indeks nie przekroczy zakresu indeksów tablicy. Ten sam indeks moŜ-
na  podawać  wielokrotnie.  Na  koniec  program  wypisuje  wszystkie  dane 
oraz niszczy zmienne dynamiczne. 

Zad. 8.2. Zadeklarować tablicę wskaźników do obiektów. Dane: tablica 100 ko-

mórek  typu 

class,  w  klasie:  imię  i  nazwisko  typu  string[40]  oraz  kon-

struktor. Program ma czytać indeks komórki do wypełnienia. Dane poda-
jemy  dopóty,  dopóki  indeks  nie  przekroczy  zakresu  indeksów  tablicy. 
Na koniec program wypisuje wszystkie dane i niszczy obiekty. 

Zad. 8.3. Zaprogramować  stos  liczb  zespolonych.  W  tym  celu  zdefiniować 

wierzchołek St typu PStos oraz PStos = ^TStos; TZesp = 

record re, im : 

Real; 

end; W programie uwzględnić następujące operacje:  

a)

 

wkładanie na stos 

 

procedure  push(var St : PStos; D : TZesp);  

b)

 

zdejmowanie ze stosu 

 

function  pop(var St : PStos) : TZesp; 

background image

122 

 

c)

 

dodawanie, odejmowanie i mnoŜenie pary liczb z wierzchołka stosu 

  

function dod(a,b : TZesp): TZesp; function odej(a, b : TZesp): TZesp; 

W programie głównym zaprogramować obliczanie wyraŜenia 

c := a + (b 

* c) w odwrotnej notacji polskiej (ONP). 

Zad. 8.4. Zaprogramować  stos  tak,  jak  w  zad.  8.3,  ale  uŜyć  typu 

class  zamiast 

record.  Zdefiniować  klasy:  TZesp,  TElemSt  i  TStos.  W  klasie  TZesp, 
oprócz  konstruktora  Create,  ustawiającego  pola  re,  im,  zdefiniować 
dodatkowy 

constructor dod(a,  b  :  TZesp);  i  podobnie 

  odejm  i  mnoz. 

W  tych  konstruktorach  naleŜy  takŜe  wywołać 

inherited  Create;  oraz 

(na  końcu)  a.Free;  Uwaga:  zmienna 

var  c  :  TZesp;  jest  wskaźnikiem  do 

obiektu, więc nie trzeba dla niej wywoływać konstruktora, poniewaŜ w in-
strukcji c := St.pop; przekaŜemy do c adres istniejącego obiektu (stworzo-
nego konstruktorem dod). 

Zad. 8.5. Zaprogramować  listę  jednokierunkową  na  wskaźnikach.  Dane:  imię, 

nazwisko i rok urodzenia. W programie uwzględnić operacje:  

a)  dodawania osoby, tak Ŝe nazwiska są ułoŜone rosnąco alfabetycznie, 

b)  usuwania osoby o określonym nazwisku, 

c)  wyświetlania całej listy, 

d)  usuwania całej listy. 

Zad. 8.6. Zaprogramować  listę  jednokierunkową  na  klasach.  W  klasie  zadekla-

rować: imię, nazwisko i wiek. Uwzględnić operacje:  

a)  dodawania osoby, tak Ŝe nazwiska są ułoŜone rosnąco alfabetycznie, 

b)  usuwania osoby o określonym nazwisku, 

c)  wyświetlania całej listy, 

d)  usuwania całej listy. 

Zad. 8.7. Zaprogramować kolejkę na klasach. W klasie zadeklarować: imię, na-

zwisko oraz rok studiów. W programie uwzględnić operacje:  

a)  dodawania elementu na końcu kolejki, 

b)  usuwania pierwszego elementu kolejki,  

c)  wyświetlania całej kolejki, 

d)  usuwanie całej kolejki. 

 
 

 

background image

123 

 

9.

 

Klasy 

 

Klasa  jest  złoŜoną  strukturą  danych,  rozbudowaną  o  funkcje  składowe. 

Klasa

 jest wzorcem, tzn. typem dla swoich zmiennych, czyli obiektów. Obiek-

tem nazywamy konkretny egzemplarz danej klasy. Klasę tworzą: dane skła-
dowe  (zmienne)  oraz  funkcje  składowe  (metody),  w  tym  specjalne  metody 

 

konstruktory i destruktory. 
 

9.1.

 

Zasady deklarowania klasy 

Klasę deklaruje się przy uŜyciu słowa kluczowego 

class, mianowicie: 

 
type  TNazwaKlasy = class … end; 
 
W  dobrym  stylu  jest  nadawanie  klasie  nazwy  z  duŜej  litery  T.  Elementy 

klasy  są  podzielone  na  sekcje:  publiczną,  prywatną  i  chronioną.  Elementy  te 
mogą  być  deklarowane  w  dowolnej  liczbie  występujących  po  sobie  sekcji  wy-
dzielonych przez etykiety 

public lub private

Elementy  klasy  są  domyślnie  prywatne.  Metod  i  zmiennych  publicz-

nych  klasy  moŜna  uŜywać  wszędzie  w  programie,  natomiast  do  metod  i 
zmiennych prywatnych dost
ęp mają tylko inne metody klasy. Ponadto: 

 

klasa powinna być abstrakcyjnym typem danych, czyli takim, który raz 
zaprojektowany moŜe być uŜywany przez innych programistów bez ko-
nieczności wgłębiania się w jego mechanizm, 

 

sekcja  publiczna  deklaracji  klasy  nazywana  jest  interfejsem  klasy.  Za-
warte w niej metody, wraz z opisami ich parametrów, działanie i format 
wyniku powinny dawać programiście minimum informacji o klasie, 

 

według  zasady abstrakcyjności danych, wszystkie dane składowe klasy 
powinny znajdować się w sekcji prywatnej, 

 

w  sekcji  publicznej  naleŜy  umieszczać  metody  operujące  na  danych 
prywatnych, 

 

jeśli  metoda  publiczna  wykonuje  wiele  obliczeń  lub  pewne  z  nich  są 
wykonywane przez wiele metod publicznych, to moŜna je wyodrębnić w 
postaci  osobnej  metody  prywatnej.  Są  one  wtedy  wyłącznie  na  uŜytek 
implementacji klasy.  

 

implementacja klasy powinna być zaprojektowana przez programistę w 
sposób, który daje mu moŜliwość rozbudowania i udoskonalania działa-

background image

124 

 

nia  funkcji  jej  składowych,  bez  wprowadzania  jakichkolwiek  zmian  w 
interfejsie, 

 

dostęp do danych i funkcji składowych obiektu uzyskuje się przez uŜy-
cie operatora dostępu ’.’ (kropki). 

 
NajwaŜniejsze

 sekcje w obrębie klasy to

 

public – definiuje pola i metody publiczne, które są dostępne z dowol-
nego miejsca programu oraz z modułu, w którym zostały zdefiniowane, 

 

private – definiuje pola i metody prywatne, które są niedostępne spoza 
modułu (pliku kodu źródłowego), w którym zdefiniowano klasę, 

 

protected – definiuje pola i metody chronione, które są widoczne tylko 
w danej klasie i w klasach pochodnych (tych, które z niej dziedziczą). 

 

9.2.

 

Istota programowania obiektowego 

Programowanie  obiektowe  to  programowanie,  w  którym  klasy  stanowią 

najwaŜniejszą  część w konstrukcji programu. Podstawowe cechy programowa-
nia obiektowego to: 

 

 dziedziczenie, 

 

 hermetyzacja, 

 

 polimorfizm, 

 

 rozszerzalność. 

 

Dziedziczenie  to  budowa  jednej  klasy  na  bazie  drugiej,  przez  dodawa-

nie/przesłanianie  składowych  klasy  bazowej.  Dziedziczenie  umoŜliwia  klasie 
pochodnej  uzyskanie  dostępu  do  pól i  metod  swoich klas  rodzicielskich. Klasy 
potomne  dziedziczą  charakterystyki  i  działania  klasy  rodzic.  Klasy  pochodne 
mogą zmieniać operacje odziedziczone i mogą definiować nowe. 

Hermetyzacja (inkapsulacja) to cecha, która pozwala na łączenie danych z 

metodami w obrębie obiektu. 

Polimorfizm to moŜliwość dzielenia pojedynczego działania i nazwy dzia-

łania  poprzez  hierarchię  obiektową  w  sposób  właściwy  dla  kaŜdego  obiektu  w 
tej  hierarchii.  Polimorfizm  pozwala  kaŜdej  klasie  na  posiadanie  własnych  po-
trzeb. Metody te zapewniają jednolitą odpowiedź na komunikaty dochodzące do 
Ŝą

danych klas w hierarchii. Polimorfizm umoŜliwia obiektom generowanie wła-

snych odpowiedzi na podobne komunikaty. 

Rozszerzalność pozwala rozszerzać i uaktualniać skompilowane moduły. 

 

 

background image

125 

 

Przykład 1. Deklaracja klasy z róŜnymi sekcjami. 
 

Program figura_10; 
type 
 
    TFigura = class  
    private  
        bok : Byte
        podstawa : 1..100; 
        procedure wypisz; 
    protected  
        kat : Integer
        ile_pr : 0..2; 
        function pole(a,b : Byte) : Integer
    public 
        nazwa : string;  
        procedure init; 
        procedure free; 
    end

 
Przykład 2. Ilustracja definicji metod klasy

 

program figura_02; 
type 
  
TProstokat = class  
    a, b : Integer;        // wymiary prostok

ą

ta  

    function obwod : Integer
    function pole  : Integer
  end; 
 
function TProstokat.obwod : Integer
  begin 
    Result := (a+b)*2; 
  end; 
 
function TProstokat.pole : Integer
  begin 
    Result := a*b; 
  end; 
 
var  Pro1, Pro2 : TProstokat; 
 
BEGIN 
  Pro1 := Tprostokat.Create; 
  Pro1.a := 4; 
  Pro1.b := 2; 

background image

126 

 

  // Pro2 := Tprostokat.Create; 
  // pro2.a := 10; pro2.b := 20; // 

ą

d! brak Create! 

  writeln('obwod= ', Pro1.obwod); 
  writeln('Pole = ', Pro1.pole); 
  //writeln('Pole2= ', Pro2.pole); 
  Pro1.Free; 
  readln; 
END. 

 

Przykład 3.  UŜycie dwóch klas w jednym programie. 
 

program figury_03; 
type 
  
TProstokat = class  
    a, b : Integer;        // wymiary prostok

ą

ta  

    function obwod : Integer
    function pole  : Integer
  end
 
function TProstokat.obwod : Integer
  begin 
    Result := (a+b)*2; 
  end; 
 
function TProstokat.pole : Integer
  begin 
     Result := a*b; 
  end; 
 
type  
  Trojkat = class 
   a, b, c : Integer;      // wymiary trójk

ą

ta 

   function obwod : Integer
   function pole  : Integer
  end; 
  
function Trojkat.obwod : Integer
  begin 
    Result := a+b+c; 
  end; 
 
function Trojkat.pole : Integer
  begin 
    Result := a*(b+c) div 2; 
  end; 

background image

127 

 

 
var
  prost1 : TProstokat; // zmienne obiektowe 
     trojk1 : Trojkat; 
BEGIN 
  prost1 := TProstokat.Create; 
  // obowi

ą

zkowe wywołanie konstruktora 

  prost1.a := 5; 
  prost1.b := 7; 
  writeln('obwod pr= ', prost1.obwod); 
  writeln('Pole pr = ', prost1.pole); 
  readln; 
  trojk1 := Trojkat.Create; 
  trojk1.a := 2; 
  trojk1.b := 3; 
  trojk1.c := 4; 
  writeln('obwod tr= ', trojk1.obwod); 
  writeln('Pole  tr= ', trojk1.pole); 
  prost1.Free;   // obowi

ą

zkowe 

  trojk1.Free;   // obowi

ą

zkowe 

  readln; 
END. 

 

Przykład 4. Ilustracja róŜnych sposobów dostępu do pól klasy. 
 

program PrKonstr4; 
type  
TProstokat = class 
  a : Integer
  procedure ustaw(_d : Integer); 
  function wypisz : Integer
 private 
  d : Integer
 protected  
  b : Integer
 public 
  c : Integer
 end; 
 
 procedure TProstokat.ustaw(_d : Integer); 
  begin 
    d := _d; 
  end; 
  
function TProstokat.wypisz : Integer
  begin 

background image

128 

 

    Result := d; 
  end; 
var
   Pro1 : TProstokat; 
BEGIN  // PROGRAM GŁÓWNY 
  Pro1 := Tprostokat.Create; 
  Pro1.a := 4;   
  Pro1.b := 2;// w module jest dost

ę

p do pola protected 

  Pro1.c := 50; 
  // Pro1.d := 1; // brak dost

ę

pu do pola prywatnego 

  writeln('a = ', Pro1.a); 
  writeln('b = ', Pro1.b); 
  writeln('c = ', Pro1.c); 
  Pro1.ustaw(100); 
  writeln('d = ', Pro1.Wypisz); 
  readln; 
  Pro1.Free 
END. 

 

9.3.

 

Konstruktor i destruktor

 

 

konstruktor 

Create tworzy obiekt (przydziela wymaganą pamięć), 

 

destruktor 

Free  zwalnia przydzieloną pamięć, 

 

klasa  moŜe  zawierać  więcej  niŜ jeden  konstruktor,  a powinna  zawierać 
jeden destruktor, 

 

konstruktor powinien być wywołany jako pierwsza metoda dla nowego 
obiektu. 

 

wywołanie  destruktora  kończy  pracę  z  obiektem;  aby  ponownie  korzy-
stać z obiektu, naleŜy ponownie wywołać konstruktor, 

 

uŜycie konstruktora 

Create jest obowiązkowe dla kaŜdego egzemplarza 

klasy (przydziela wymaganą pamięć), np. 

TABC = 

class … end;  

… 
var ob1 : TABC; 
ob1 := TABC.Create; 

 

uŜycie destruktor 

Free jest obowiązkowe dla kaŜdego egzemplarza kla-

sy (zwalnia przydzieloną pamięć), np. 

ob1.Free; 

 

Przykład 1. UŜycie konstruktora i destruktora. 
 

program Pr_Konstructor1; 
type  TProst = class 
    x, y : Integer
    a, b : Integer; {boki

background image

129 

 

    constructor Create; 
    destructor Free; 
    function pole : Longint
  end; 
 
  constructor TProst.Create; 
    begin   // Inherited Create; 
 
      a := 20;  b := 4;  
    end; 
   
  destructor Tprost.Free; 
    begin // Inherited Free;{ koniec pracy z TKwadrat }  
    end
   
  function TProst.pole : Longint;
 
    begin  
      Result := a*b;  
    end
 
var  Pkat : TProst; 
BEGIN 
  Pkat := Tprost.Create; 
  writeln('Pole prostokata: ', Pkat.pole); 
  Pkat.a := 100; 
  writeln('Pole prostokata: ', Pkat.pole); 
  Pkat.Free; 
  readln;  
END. 

 
Przykład 2. UŜycie destruktora i konstruktora. 
 

program pr_konstruktor2; 
type
  TProst = class  
    a, b : Integer;  {boki prostok

ą

ta

    constructor Create(_a,_b : Integer); 
    destructor Destroy; 
    function pole : Longint
  end
  
constructor TProst.Create(_a, _b : Integer); 
  begin 
    Inherited Create;  // nieobowi

ą

zkowe  

    a := _a; 
    b := _b; 
    writeln('Działa konstruktor TProst'); 

background image

130 

 

  end
 
destructor TProst.Destroy; 
  begin 
    writeln('Destruktor - Koniec pracy z TProst'); 
    Inherited Destroy; 
  end
 
function TProst.pole : Longint
  begin 
    pole := a*b; 
  end; 
 
var  p1, p2 : TProst; 
BEGIN 
  p1 := Tprost.Create(20,30); 
  p2 := Tprost.Create(4,7); 
  writeln('Pole prostokata   : ', P1.pole); 
  p1.Destroy;  // Pkat.Free; 
  p2.Destroy; 
  readln; 
END. 

 

9.4.

 

Typy konstruktorów 

 

JeŜeli  nie  zostanie  zdefiniowany  przez  uŜytkownika  w  klasie  Ŝaden  kon-

struktor,  wówczas  kompilator  wykorzysta  konstruktor  klasy  TObject,  która jest  
nadrzędna dla wszystkich klas. 

WyróŜniamy trzy rodzaje konstruktorów: 

 

domyślny (bez parametrów), 

 

zwykły (z parametrami) 

 moŜe być wiele takich konstruktorów, 

 

kopiujący. 

 

Jeśli  zadeklarujemy  jakikolwiek  konstruktor  pobierający  parametry,  wtedy 

musimy takŜe zadeklarować konstruktor domyślny (bezparametrowy). Przy de-
finicji konstruktora domyślnego wskazane jest inicjalizowanie danych. 

 
Przykład. 1. Program pokazuje uŜycie kilku konstruktorów w jednej klasie. 
 

Program konstruktory; 
type 
  TPot = class 
  private 

background image

131 

 

    x, y Integer
  public 
    constructor Create;  overload;  //domy

ś

lny 

    constructor Create(dx, dy : Integer);  overload
    //zwykły 
    constructor Create(const dxy : TPot);  overload
    //kopiuj

ą

cy 

end

 

Przykład 2. UŜycie konstruktora zwykłego i domyślnego. 
 

program Pr_Konstruktor3; 
uses
  SysUtils; 
type 
   TFig = class 

     bok : ^Byte;    //  

konstruktor zwykły 

     constructor  Create(x : Byte); overload
     constructor  Create; overload

     destructor Free; //  

konstruktor domy

ś

lny 

     procedure wypisz; 
     function pole : Integer
   end; 
 
constructor TFig.Create; 
  begin 
    New(bok); 
  end
 
constructor TFig.Create(x : Byte); 
  begin 
    Create; 
    bok^ := x; 
  end
 
destructor TFig.Free; 
  begin 
    Dispose(bok); 
  end
 
procedure TFig.wypisz; 
  begin 
    writeln('bok = ', bok^); 
  end; 
 
function Tfig.pole : Integer

background image

132 

 

  begin 
    Result := bok^ * bok^; 
  end; 
 
var  kwadrat : TFig; 
BEGIN 
  kwadrat := TFig.Create(5); 
  kwadrat.wypisz; 
  kriteln('Pole kwadratu=', kwadrat.pole) 
  kwadrat.Destroy; 
END

 

Przykład 3. UŜycie konstruktora domyślnego, zwykłego i kopiującego. 
 

Program Pr_Konstruktor4; 
type  
 TPot = class  
  private  
    x, y : Integer
  public 

  constructor Create; overload; //

domy

ś

lny //

↓↓↓↓

zwykły 

  constructor Create(dx, dy : Integer); overload; 
  constructor Create(const DK : TPot); overload; 

 end;         //

kopiuj

ą

cy 

 
constructor TPot.Create; 
  begin  
   inherited Create; 
   x := 1;  y := 0; 
  end
 
constructor TPot.Create(
dx, dy : Integer); 
  begin  
   inherited Create; 
   x := dx; 
   y := dy; 
  end
 
constructor TPot.Create(const DK : TPot); 
  begin 
   inherited Create; 
   x := DK.x; 
   y := DK.y; 
  end
 

background image

133 

 

var  a, b, c, d : TPot; 
BEGIN  
  a := TPot.Create; 
  b := TPot.Create( 7,13 ); 
  c := TPot.Create( b ); 
  // tworzenie obiektu przez kopiowanie obiektu b 
  // d := TPot.Create; 

  

d := b; // d jest tylko wska

ź

nikiem na obiekt b 

  writeln(' a: ',a.x,' ',a.y); 
  writeln(' b: ',b.x,' ',b.y); 
  writeln(' c ',c.x,' ',c.y); 
  writeln(' d ',d.x,' ',d.y); 
  a.Free; 
  b.Free; 
  c.Free; 
  // d.Free; // d pokazuje na to samo, co b (d=b) 
  readln 
END.

 

 

9.5.

 

Dziedziczenie 

W  dziedziczeniu  nowy  typ  klasy  tworzony  jest  na bazie  istniejącego  typu. 

Nowo zdefiniowany typ klasy dziedziczy wszystkie pola i metody bez koniecz-
ności ich definiowania. Typ, od którego dziedziczona jest struktura, nazywa się 
typem rodzicem  (ang. ancestor type) lub typem bazowym. Typ tworzony od ty-
pu bazowego to typ potomny (ang. descendant type) lub typ pochodny. Dziedzi-
czenie moŜe być pośrednie albo bezpośrednie. 

Dziedziczenie metod moŜe być dwojakiego rodzaju: 

 

bez przesłaniania 

 deklaracje metod w typie przodka i potomka zawie-

rają inne identyfikatory, 

 

z przesłanianiem metod 

 deklaracja metody w typie potomnym ma taki 

sam  identyfikator,  jak  metoda  w  typie  bazowym.  Wówczas  metoda  w 
typie potomnym przesłania (redefiniuje) metodę odziedziczoną od typu 
przodka. 

 

Pola klasy nie mogą być przesłaniane, tzn. Ŝaden typ potomny nie moŜe 

posiadać pól o identyfikatorach występujących w typie bazowym. Przesłonięcie 
metody statycznej dopuszcza zmiany nagłówka w dowolny sposób. Nowa meto-
da moŜe mieć zarówno inne parametry jak i róŜną treść. 

Procedura nadająca wartości polom w typie pochodnym moŜe wykorzysty-

wać procedurę nadającą wartości polom typu bazowego i określać tylko dodat-
kowe pola typu pochodnego. 

background image

134 

 

Przykład.  Aby  program  działał  prawidłowo,  naleŜy  klasę  potomka  uzupełnić 
o metodę wykonaj, o identycznej treści jak w klasie TRodzic. 
 

Program dziedicz_1; 
type  TRodzic = class 
        procedure napisz; 
      end
 
procedure TRodzic.napisz; 
  begin 
    writeln('to napisał Rodzic'); 
  end; 
 
type  TPotomek = class(TRodzic) 
        procedure napisz; 
      end
 
procedure TPotomek.napisz; 
  begin 
    Inherited napisz; 
    writeln('To dopisało 

Ŝ

ycie (Potomek)'); 

    //writeln('A to dopisało 

Ŝ

ycie (Potomek)'); 

  end
 
var  potomek : TPotomek; 
 
BEGIN 
  Potomek := TPotomek.Create; 
  Potomek.napisz; 
  readln;   
  Potomek.Free; 
END. 

 

9.6.

 

Polimorfizm

 

Polimorfizm  (wielopostaciowość)  jest  mechanizmem,  dzięki  któremu  ta 

sama metoda (o tej samej nazwie (i parametrach)) moŜe mieć róŜne skutki dzia-
łania (róŜne znaczenie) w zaleŜności od tego, jaki obiekt ją wywołuje. 

Dziedziczenie homomorficzne zachodzi wtedy, gdy połączenia miedzy me-

todami obiektów są wykonywane w trakcie kompilacji programu. 

Dziedziczenie polimorficzne polega na korzystaniu z tablic metod wirtual-

nych  (VMT)  dla  kaŜdej  klasy,  w  której  są  adresy  metod  w  danej  klasie. 
Wzajemne  wywoływanie metod  odbywa  się  za  pośrednictwem  adresów  zawar-

background image

135 

 

tych w tablicach VMT, a te są róŜne dla klasy przodka i potomka. Metoda w kla-
sie  przodka  prawidłowo  rozpozna  wywoływaną  metodę  równieŜ  w  klasie  po-
tomka. 

W  dziedziczeniu  polimorficznym  naleŜy  uŜywać  metod  wirtualnych, 

tj. w nagłówku metody przodka trzeba wpisać dyrektywę 

virtual,  np.  procedu-

re  metoda; virtual; ale w deklaracji nagłówka metody potomka naleŜy uŜyć dy-
rektywy 

override, np.  procedure  metoda; override;  

 
Przykłady dziedziczenia: 
 

Dziedziczenie homomorficzne 

  

Dziedziczenie polimorficzne 

program dziedzicz_homomorf; 
type   TRodzic = class  
    procedure napisz;   
    procedure wykonaj;   
end; 
procedure
 TRodzic.napisz; 
  begin  
    writeln('To napisał >> Rodzic'); 
  end; 
  procedure
 TRodzic.wykonaj; 
  begin  
     napisz;  
  end; 
 
type   TPotomek = class(TRodzic)
 
   procedure napisz;  
 end; 
procedure
 TPotomek.napisz; 
 begin 
   writeln('to napisał > Potomek' ); 
 end; 
 
var 
  potomek :TPotomek; 
 
BEGIN
 
  Potomek:= TPotomek.Create; 
  Potomek.wykonaj; 
  Readln; 
  Potomek.Free; 
END. 

 

program dziedzicz_polimorf; 
type   TRodzic = class  
    procedure napisz;  virtual; 
    procedure wykonaj;   
end; 
procedure
 TRodzic.napisz; 
  begin  
    writeln('To napisał >> Rodzic'); 
  end; 
  procedure TRodzic.wykonaj; 
  begin  
    napisz;  
  end; 
 
type   TPotomek = class(TRodzic)
 
   procedure napisz;  override; 
 end; 
procedure
 TPotomek.napisz; 
 begin 
   writeln('to napisał > Potomek' ); 
 end; 
 
var 
  potomek :TPotomek; 
 
BEGIN
 
 Potomek:= TPotomek.Create; 
 Potomek.wykonaj; 
 Readln; 
 Potomek.Free; 
END. 
 

background image

136 

 

9.7.

 

Metody wirtualne 

Lazarus FPC metody wirtualne mogą być równieŜ pokrywane metodami 

statycznymi  albo  innymi  metodami  wirtualnymi.  Z  tego  powodu,  przy  dziedzi-
czeniu  polimorficznym,  konieczne  jest  uŜywanie  dyrektywy 

override  w  klasie 

potomka. JeŜeli metodę wirtualną zakrywamy metodą statyczną (brak 

override), 

rezygnujemy z polimorfizmu i kompilator wyświetla ostrzeŜenie, dlatego dyrek-
tywę 

override naleŜy zastąpić dyrektywą reintroduce

W  dziedziczeniu  polimorficznym  naleŜy  uŜywać  metod  wirtualnych. 

W nagłówku metody przodka naleŜy uŜyć dyrektywy 

virtual, np.

procedure  metoda;  virtual; 

W deklaracji nagłówka metody potomka naleŜy jednak uŜyć 

override, np.

procedure  metoda;  override; 

 

Przykład 1. Ilustracja uŜycia dyrektywy reintroduce
 

program dziedzicz_reintro; 
type TRodzic = class 
       procedure dodaj(x : Byte); virtual
     end
 
procedure TRodzic.dodaj(x : Byte); 
  begin 
    writeln(x+10); 
  end
 
type TPotomek = class(TRodzic) 
      procedure dodaj(x : Byte); reintroduce
     end
 
procedure TPotomek.dodaj(x : Byte); 
  begin   
    writeln(x, ' + dziesiec'); 
  end
 
var  potomek : TPotomek; 
BEGIN 
  Potomek := TPotomek.Create; 
  Potomek.dodaj(1); 
  readln; 
  Potomek.Free; 
END. 

background image

137 

 

Przykład 2. Dziedziczenie na zasadzie   rodzic 

 potomek 

 wnuk. 

 

program dziedziczenie_wnuk;  
type  TRodzic = class  
        procedure dodaj(x : Byte); virtual
        procedure wykonaj(b : Byte); 
      end
 
procedure  TRodzic.dodaj(x : Byte); 
  begin 
    writeln(x+10); 
  end
 
procedure  TRodzic.wykonaj(b : Byte); 
  begin 
    dodaj(b);   
  end
 
type TPotomek = class(TRodzic
       procedure dodaj(x : Byte); override
     end
 
procedure TPotomek.dodaj(x : Byte); 
  begin  
    writeln(x,' + sto'); 
  end; 
 
type TWnuk = class(TPotomek
       procedure dodaj(x : Byte); override
     end; 
 
procedure TWnuk.dodaj(x : Byte); 
  begin  
    writeln(x,' plus 1000'); 
  end; 
 
var  Pot : TRodzic; 
 
BEGIN 
  Pot := TRodzic.Create; 
  Pot.wykonaj(1); 
  Pot.Free;  
  Pot := TPotomek.Create; 
  Pot.wykonaj(1); 
  Pot.Free;  

background image

138 

 

  Pot := TWnuk.Create; 
  Pot.wykonaj(1); 
  Pot.Free; 
  readln; 
END. 

 

9.8.

 

Metody dynamiczne 

Dziedziczenie polimorficzne zapewniają: 

 

 metody wirtualne deklarowane słowem 

virtual,  

 

 metody dynamiczne deklarowane słowem 

dynamic.  

 
Metody te działają tak samo, ale róŜnią się sposobem optymalizacji. Meto-

da dynamiczna daje mniejszy rozmiar kodu wynikowego programu po kompila-
cji,  natomiast  metoda  wirtualna  powoduje,  Ŝe  program  po  kompilacji  działa 
szybciej  niŜ  dla  metody  dynamicznej.  Z  polimorfizmem  mamy  do  czynienia 
równieŜ  w  przypadku  wywołania  metod  z  parametrem  aktualnym  typu  potom-
nego względem typu parametru formalnego. 
 
Przykład.  Dziedziczenie na zasadzie  Fig(Figura) 

 Pr(Prostokąt). 

 

program dziedzicz_dyn; 
type  TFig = class  
       x, y : Integer
       nazwa: string[20];    
       constructor Create; 
       destructor Destroy; 
       procedure narysuj; 
       procedure zmaz; 
     end
 
constructor TFig.Create; 
  begin  
    inherited Create; 
    x := 1; y := 2; 
  end
 
destructor TFig.Destroy; 
  begin  zmaz; 
    inherited Destroy;  
  end
 
 
 

background image

139 

 

procedure TFig.narysuj; 
  begin  
    writeln('Narysowano figur

ę

  x=',x,' y=',y); 

  end
 
procedure TFig.zmaz; 
  begin  
    writeln('Zmazano figure x=',x,' y=',y,' ',nazwa); 
  end
  //  klasy dziedzicz

ą

ce z TFig  

type  TProst = class(TFig) 
    a, b : Integer
    constructor Create(a_, b_: Integer; name : string); 
    procedure narysuj; 
    procedure zmaz_np; 
    procedure przesun(x1,y1 : Integer); 
   end
 
constructor TProst.Create(a_,b_:Integer; name :string); 
  begin   
    inherited Create; 
    a := a_;  b := b_;  
    nazwa:=name;  
  end
 
procedure TProst.narysuj; 
  begin  
    write('+ Rysowanie prostok

ą

ta  x=',x,' y=',y); 

    writeln('+ o rozmiarach  a=',a,' b=',b); 
  end
 
procedure TProst.zmaz_np; 
  begin  
  writeln('Zmazano prostok

ą

t x=',x, 'y=',y, ' ',nazwa); 

  end
 
procedure TProst.przesun(x1,y1 : Integer); 
  begin  
    zmaz; // zmaz_np
    x := x1;  y := y1; 
    narysuj; 
  end
 
 
 
 

background image

140 

 

var Fig : TFig; 
    Pr1 : TProst; 
 
BEGIN 
  Fig := TFig.Create; 
  Fig.narysuj; 
  Fig.Destroy; // Fig.Free; 
  Fig := TProst.Create(2,2,'Kwadrat'); 
  Fig.narysuj;  // Fig.przesun(10, 10); 
  Fig.Destroy; 
  Pr1 := Tprost.Create(10,15,'Prost 1'); 
  Pr1.narysuj; 
  Pr1.przesun(100, 100); 
  Pr1.zmaz_np; 
  Pr1.zmaz; 
  Pr1.Free; 
  readln; 
 END. 

 

 

background image

141 

 

10.

 

Programowanie obiektowe - aplikacje  

 

LCL jest biblioteką wizualnych komponentów, stworzoną w języku Object 

Pascal,  na potrzeby środowiska Lazarus

Biblioteka LCL naleŜy do jednych z 

bardziej  przejrzystych  i  dobrze  zaprojektowanych  bibliotek  wspomagających 
programowanie  w  środowisku  Lazarus,  zwłaszcza  tworzenie  interfejsu  uŜyt-
kownika. Podstawą biblioteki LCL jest klasa bazowa TClass. 

VCL jest biblioteką wizualnych komponentów, stworzoną w języku Object 

Pascal przez firmę Borland, na potrzeby środowiska Delphi, a później zaadap-
towaną równieŜ do środowiska C++ Builder. Obecnie biblioteka VCL integruje 
w sobie dodatkowo moŜliwość korzystania z technologii .NET firmy Microsoft
Podstawą biblioteki VCL jest klasa bazowa TObject. Z niej dziedziczą wszystkie 
pozostałe klasy biblioteki. 

VCL  to  skrót  od  ang.  Visual  Component  Library  (Biblioteka  Wizualnych 

Komponentów).  Jest  jednym  z  najwaŜniejszych  elementów  Delphi.  To  właśnie 
niej zawdzięczamy łatwość i szybkość projektowania aplikacji. Biblioteka VCL 
składa  się  z  gotowych,  napisanych  wcześniej,  komponentów,  które  podczas 
kompilacji są dołączane do wykonywalnego pliku naszego programu. Po skom-
pilowaniu  program  nie  wymaga  Ŝadnych  dodatkowych  plików.  MoŜe  być  uru-
chamiany  na  dowolnym  komputerze,  bez  względu  na  rodzaj  zastosowanych 
komponentów. 

Standardowe komponenty VCL/LCL nadają się do większości zastosowań. 

Gdybyśmy  jednak  potrzebowali  jakichś  specjalnych  funkcji,  moŜemy  doinsta-
lować  dodatkowe  komponenty  (w  sieci  jest  ich  bardzo  wiele)  lub  napisać  wła-
sny.  Zbiór komponentów to zbiór elementów, z  których poprzez odpowiednich 
ich dobór, moŜna w krótkim czasie zbudować aplikację. Komponenty posiadają 
określone  metody,  właściwości  oraz  zdarzenia.  Te  pojęcia  określają  sposób 
komunikowania się komponentu z otoczeniem. 

 

10.1.

 

Komponenty LCL

 

Ś

rodowisko Lazarus zawiera zbiór podstawowych komponentów wykorzy-

stywanych do tworzenia aplikacji. Są to przyciski, okienka, napisy, suwaki, pola 
wyboru,  menu,  tabelki  itp.  Komponenty  zgrupowane  są  w  kilku  zakładkach. 
Istnieje moŜliwość dodania nowych komponentów i stworzenia własnych. Kom-
ponenty LCL znajdują się na pasku, pod listwą Menu (rys. 10.1). 
 

 

background image

142 

 

 

Rys. 10.1. Widok paska menu środowiska Lazarus

 

 
Grupa  Standard  zawiera najczęściej  wykorzystywane  komponenty.  SłuŜą  one 
do projektowania wyglądu aplikacji. Oto jej składniki: 

TFrames - Ramki (frames)  mają podobne 
właściwości jak formularze (forms), z tym 
wyjątkiem,  Ŝe  ramka  moŜe  być  osadzona 
wewnątrz  formy.  Wprowadzenie  ramek 
bardzo  ułatwiło  projektowanie  wyglądu 
niektórych aplikacji. 

TMainMenu  -  główne  menu  danego  for-
mularza. 

TPopUpMenu  -  menu  wyświetlane  po 
kliknięciu prawym przyciskiem myszki na 
danym obiekcie. 

TLabel  –  (etykieta)  pole  słuŜące  do  wy-
ś

wietlania tekstu. 

TEdit  -  pole  słuŜące  do  edycji  jednego 
wiersza tekstu. 

TMemo  -  pole  tekstowe  z  moŜliwością 
edycji,  odczytu  i  zapisu  wyświetlanego 
tekstu. 

TButton – przycisk. 

TCheckBox - pole wyboru. 

TRadioButton  -  pole  wyboru jednej  z  kil-
ku opcji. 

TListBox - wyświetla listę elementów. 

TComboBox  -  wyświetla  listę  elementów 
z moŜliwością wpisania tekstu. 

TScrollBar - pasek przewijania.

 

Rys. 10.2. Widok okna komponentów  

– zakładka Standard 

 

background image

143 

 

TGroupBox - grupuje kilka komponentów np. typu RadioButton lub CheckBox.  
TRadioGroup - grupuje komponenty RadioButton. 
TPanel  -    komponent  ogólny  w  kształcie  prostokąta, w  którym  moŜna  umiesz-
czać inne komponenty. 
ActionList  -  komponent  pozwalający  na  dodawanie  własnych  procedur  obsługi 
do niektórych akcji wykonywanych przez uŜytkownika. 
 
Grupa Additional zawiera uzupełniające kom-
ponenty,  kształtujące  wygląd  naszej  aplikacji 
oraz ułatwiające komunikację z uŜytkownikiem. 
TBitBtn – przycisk, na którym umieszczony jest 
rysunek (Ikona). 
TSpeedButton  -  przycisk  umieszczany  w  pasku 
narzędzi. 
TMaskEdit  -  pole  edycji  pozwalające  na  filtro-
wanie  i  formatowanie  danych  wprowadzanych 
przez uŜytkownika. 
TStringGrid  –  arkusz,  którego  elementami  są 
łańcuchy znaków. 
TDrawGrid - arkusz przeznaczony do wprowa-
dzania danych innych niŜ tekstowe. 
TImage - komponent wyświetlający grafikę. 
TShape - figura geometryczna. 
TBevel - tworzy wypukłe lub wklęsłe linie, pro-
stokąty, lub ramki. 
TScrollBox  -  przewijane  okienko  mogące  za-
wierać inne komponenty. 
TCheckListBox - przewijana lista z moŜliwością 
zaznaczenia poszczególnych pozycji. 
TSplitter - słuŜy do przesuwania części okienka. 
TStaticText  –  tekst  statyczny,  komponent  dzia-
łający podobnie jak Label. 
TControlBar - pasek narzędzi z moŜliwością 
przestawiania poszczególnych pozycji. 
ApplicationEvents - niewizualny komponent 
umoŜliwiający obsługę globalnych zdarzeń 
aplikacji. 
TLabeledEdit - pole edycyjne z tekstem opisu. 
TColorBox - okno kolorów systemowych. 

Rys. 10.3. Widok okna komponentów  

– zakładka Additional

 

background image

144 

 

Grupa System 

Na tej karcie znajdują się komponenty korzystające bezpośrednio z funkcji 

systemowych, np. TTimer wywołuje zadaną procedurę w określonych odstępach 
czasu. 
 
Grupa Dialogs zawiera komponenty wywo-
łujące róŜnego typu okienka dialogowe: 
 

TOpenDialog - okienko otwierania pliku. 
TSaveDialog - okienko zapisywania pliku. 

TOpenPictureDialog - okienko otwierania 
pliku z podglądem graficznym. 

TSavePictureDialog - okienko zapisywania 
pliku z podglądem graficznym. 

TFontDialog - okienko wyboru czcionki. 
TColorDialog - okienko wyboru koloru. 

TPrintDialog - okienko drukowania. 
TPrinterSetupDialog - okienko ustawień dru-
karki. 

TFindDialog - okienko obsługujące procedu-
ry przeszukiwania. 

TReplaceDialog - okienko obsługujące pro-
cedury zamiany zadanej frazy. 

TPageSetupDialog - okienko ustawień strony. 
 

Rys. 10.4. Widok okna komponentów  

– zakładka Dialogs

 

 

10.2.

 

Inspektor obiektów

 

Inspektor  obiektów  pozwala  zobaczyć  wszystkie  komponenty  uŜyte  w 

programie. Pokazuje jednocześnie jak są one rozmieszczone na formie. Ułatwia 
szybkie  przełączanie  się  między  nimi.  KaŜdy  obiekt  widoczny  w 

Inspektorze 

obiektów ma wyświetloną nazwę i klasę źródłową. Znajdują się w nim równieŜ 
cztery sekcje: 

WłaściwościZdarzeniaUlubioneOgraniczenia

Sekcja 

Właściwości zawiera właściwości uŜywanych obiektów. Wiele war-

tości  jest  ustalanych  domyślnie  w  trakcie  inicjalizacji  komponentu.  Wartości 
domyślne moŜna zmieniać w trakcie projektowania aplikacji lub w kodzie. 

background image

145 

 

W sekcji 

Zdarzenia moŜna generować podprogramy, które reagują na okre-

ś

lone  zdarzenia  związane  z  danym  komponentem.  Zakładka  ta  składa  się  z 

dwóch kolumn: lewa zawiera nazwy poszczególnych zdarzeń, a prawa 

 proce-

dury  przypisane  do  nich.  Na  przykład,  zdarzenie  OnClick  odpowiada  sytuacji, 
gdy  dany  komponent  zostanie  kliknięty  myszką,  a  przypisany  podprogram  wy-
kona określoną operację.  
 

10.3.

 

Okno komunikatów i okno Form

 

Komunikaty to opisy, uwagi, ostrzeŜenia i błędy generowane przez kompi-

lator Lazarus. Pokazywane są w oknie. Komunikaty stanowią podstawowe źró-
dło informacji o problemach uniemoŜliwiających uruchomienie programu. 
 

 

Rys. 10.5. Widok okna komunikatów

 

 
Okno Form: 

 

okno graficzne, na którym rozmieszcza się komponenty aplikacji, 

 

obsługuje tryb WYSIWYG (ang. What You See Is What You Get), 

 

umoŜliwia  zmianę  właściwości  danego  komponentu  oraz  wygenerowa-
nie kodu obsługi zdarzenia OnClick poprzez kliknięcie na nim. 

 

10.4.

 

Edytor źródeł 

Edytor źródeł umoŜliwia podgląd oraz edycję kodu źródłowego tworzone-

go  programu.  WaŜne  fragmenty  kodu  są  wyróŜnione  kolorem  lub  tłustym  dru-
kiem. Domyślnie są uwzględniane wcięcia w kodzie. 
 
Pliki tworzone przez edytor źródeł to: 

 

Plik główny projektu z rozszerzeniem  .LPR 

 plik tekstowy, który za-

wiera informacje o formularzach i modułach aplikacji. Znajduje się tam 
równieŜ kod, który inicjalizuje aplikację.  

 

Plik  projektu  z  rozszerzeniem    .LPI 

  plik  tekstowy,  który  zawiera  in-

formacje o konfiguracji  środowiska Lazarus w formacie xml. 

background image

146 

 

 

Plik  modułu  z  rozszerzeniem  .PAS 

  plik  tekstowy  zawierający  kod  

ź

ródłowy  modułu  w  języku  Object  Pascal.  MoŜe  być  stowarzyszony  z 

formularzem lub stanowić samodzielny składnik projektu. 

 

Plik formularza z rozszerzeniem .LFM 

 plik binarny zawierający defi-

nicję  formularza.  KaŜdy  taki  plik  powiązany  jest  z  modułem  (plik 
.PAS), zawierającym program źródłowy związany z obsługą formularza. 

 

Plik zasobów z rozszerzeniem .RES 

 binarny plik zasobów projektu. 

 

 

Rys. 10.6. Widok okna Edytora źródeł

 

 

W  efekcie  kompilacji  tworzony  jest  wykonywalny  plik  wynikowy  (pro-

gram) z rozszerzeniem .EXE. Jest to plik wygenerowany przez projekt. Tworzo-
ne są takŜe pliki modułów z rozszerzeniem .PPU. Zawierają one skompilowane 
wersje modułów zawartych w projekcie. 
 

KaŜdy program składa się z wielu plików, które mają takie same nazwy, ale 

róŜne rozszerzenia. 

Wysoce zalecane jest przechowywanie (zapisywanie) pli-

ków kaŜdego projektu w oddzielnym folderze. 
 

 

background image

147 

 

10.5.

 

Obiekt Button1 klasy TButton 

 

 

Rys. 10.7. Widok komponentu Button1

 

 

Umieszczenie  na  formie  komponentu  Button  z  palety  Standard  powoduje 

pojawienie się na formie prostokątnego przycisku z napisem Button1.  

Zaznaczenie  go  pojedynczym  kliknięciem  pozwala  w  okienku  Inspektora 

obiektów zapoznać się z jego właściwościami oraz zdarzeniami. 
 

 

Rys. 10.8. Widok okna Inspektora obiektów z komponentem Button1 klasy TButton

 

 

background image

148 

 

 

W  oknie 

Inspektora  obiektów  są  widoczne  właściwości  komponentu 

Button1.  Zmiana  właściwości  powoduje  m.in.  zmianę  jego  połoŜenia, 
rozmiaru, koloru, nazwy i wielu innych parametrów.  

 

Właściwości  przycisku  moŜna  zmieniać  bezpośrednio  z  poziomu 

In-

spektora  obiektów  (w  czasie  projektowania  aplikacji)  albo  programo-
wo, za pomocą odpowiednich instrukcji w kodzie źródłowym aplikacji. 
Przykładowe  polecenie  zmieniające  właściwość  Caption  komponentu 
Button1:   Button1.Caption := 'Nowy tekst nagłówka'; 
 

Zmiana dowolnej właściwości jest natychmiast aktualizowana. W pewnych 

przypadkach niektóre właściwości są tylko do odczytu (ang. read only), znaczy 
to Ŝe nie moŜemy danej właściwości zmienić 

 moŜemy ją jednak odczytać.  

Pełną  listę  właściwości  znajdziemy  w  systemie  pomocy.  Po  zaznaczeniu 

komponentu  i  naciśnięciu  klawisza  F1  wyświetli  się  okienko  z  opisem  dostęp-
nych metod, właściwości i zdarzeń. 

W  oknie 

Inspektora  obiektów  znajduje  się  sekcja  Zdarzenia  (ang. 

Events). KaŜdy komponent posiada swój zbiór zdarzeń, które moŜemy oprogra-
mować. Nazwa kaŜdego zdarzenia składa się z przedrostka On i tekstu informu-
jącego, czego dotyczy dane zdarzenie. 

Zdarzenia  mają  za  zadanie  sprawdzać,  czy  nie  wystąpiła  określona  czyn-

ność i w przypadku jej wystąpienia wywołać odpowiadającą jej procedurę. Kli-
kając  dwukrotnie  na  pole  obok  nazwy  wybranego  zdarzenia  przechodzimy  do 
edycji kodu procedury jego obsługi. 

Najczęściej  uŜywanym  zdarzeniem  jest  zdarzenie  OnClick  reagujące  na 

kliknięcie myszką na danym komponencie. 

Inny  dostęp  do  zdarzenia  uzyskuje się  po  dwukrotnym  kliknięciu  na  kom-

ponent.  Oprócz  właściwości  i  zdarzeń,  komponenty  posiadają  równieŜ  metody.  
Są one widoczne w 

Inspektorze obiektów, w zakładce Właściwości. Metody są 

to  funkcje  i  procedury,  które  wykonuję  na  komponencie  określone  operacje. 
Przykładowo, metodę powodującą Ŝe przycisk staje się niewidoczny wywołuje-
my następującym poleceniem:  Button1.Hide; 

 

Tabela 10.1. Lista wybranych właściwości klasy TControl 

widocznych w oknie Inspektora obiektów 

Nazwa

 

Opis 

 

Align 

 

Określa domyślne połoŜenie komponentu

 

AutoSize 

 

Automatyczne utrzymywanie pierwotnego rozmiaru komponentu 

 

background image

149 

 

Caption 

 

Napis na komponencie (tekst)

 

Height 

 

Wysokość komponentu

 

Width 

 

Szerokość komponentu

 

Color 

 

Kolor tła obiektu 

 

Cursor 

 

Kursor wyświetlany po umieszczeniu wskaźnika myszy nad 
obiektem 

 

Enabled 

 

Określa, czy obiekt jest aktywny czy teŜ nie 

 

Font 

 

Czcionka uŜywana przez komponent 

 

Hint 

 

Wskazówka (etykietka podpowiedzi), pokazywana po umiesz-
czeniu kursora nad obiektem 

 

Name 

 

Nazwa komponentu 

 

Visible 

 

Właściwość określająca, czy komponent ma być widoczny pod-
czas działania programu 

 

 

Tabela 10.2. Lista wybranych zdarzeń TControl 

widoczna w oknie Inspektora obiektów 

Nazwa

 

Opis 

 

OnClick 

 

Aplikacja podejmuje działanie po kliknięciu myszką na danym 
komponencie

 

OnClose 

Zdarzenie związane z zamykaniem okna 

OnDragDrop 

 

Zdarzenie generowane jest w momencie, gdy w komponencie na-
stępuje „upuszczenie” danych przeciąganych metodą „drag and 
drop”

 

OnKeyDown 

 

Akcja po naciśnięciu dowolnego klawisza 

 

OnKeyPress 

 

Akcja po przytrzymaniu dowolnego klawisza 

 

OnMouseDown

 

Akcja po kliknięciu w obszarze komponentu 

 

OnMouseMove 

 

Akcja po ruchu kursora myszki na komponentem  

 

OnEnter 

 

Akcja na wejście do komponentu 

 

OnExit 

 

Akcja po wyjściu z komponentu 

 

 
 

 

background image

150 

 

10.6.

 

Aplikacja Stoper 

Zbudujemy stoper elektroniczny na bazie komponentu TTimer: 

 

Stoper1:  w  tej  wersji  wykorzystane  zostaną  komponenty  TTimer  oraz 
TLabel. Stoper będzie zliczał sekundy i minuty od chwili 00:00. 

 

Stoper2: zmodyfikujemy Stoper1 tak, aby zliczanie sekund następowało 
co  200ms,  oraz  zbudujemy  funkcję  wyświetlania  czasu,  która  minuty  i 
sekundy zawsze wyświetli dwucyfrowo. 

 

Stoper3:  wykorzystamy  komponenty  TTimer,  TLabel  oraz  TButton 
(x3); przycisk 

Start (Button1) uruchomi stoper, przycisk Stop (Button2) 

zatrzyma stoper, przycisk 

Koniec (Button3) zamknie aplikację. 

 

Stoper4:  wykorzystamy  komponenty  TPanel  (x2),  TTimer,  TButton 
(x4) i TLabel; przycisk 

Reset uruchomi stoper od nowa, przycisk Stop 

zatrzyma  stoper, 

Start  ponowi  zliczanie  sekund,  przycisk  Koniec  za-

mknie  aplikację.  Cyfry  stopera  umieścimy  w  TLabel,  na  Panel1  ułoŜy-
my wszystkie przyciski, na Panel2 umieścimy stoper. 
 

Aplikacja Stoper1: 

1. Otworzyć aplikację VCL. 

2. Wstawić komponenty typu TLabel i TTimer. 

3. W polu Caption formularza Form1 wpisać „Stoper1”, zmienić nazwę kompo-

nentu Label1 na Label00_00 (zmiana w polu Name). 

4. W polu Caption komponentu Label00_00 wpisać  00:00, w polu Font ustawić 

rozmiar czcionki na 48, zmienić kolor (np. na niebieski). 

5.  Wygenerować  procedurę  Timer1Timer  klikając  dwukrotnie  na  ikonie 

komponentu Timer1 (w  

Inspektorze obiektów,  w zakładce Zdarzenia, zo-

staje włączona metoda i dołączona do kodu klasy Form1).  

6. W procedurze 

Timer1Timer wpisać poniŜszy kod zliczania sekund i minut. 

 
// procedura Timer1Timer,  która inkrementuje zmienną 

sec  

// co 1000 ms, czyli co sekundę i zlicza teŜ minuty, 
// procedura Timer1Timer pochodzi od obiektu Timer1 
 

procedure TForm1.Timer1Timer(Sender: TObject); 
begin  
inc(sek);   //zliczamy  co 1000ms  
  if sek = 60 then  
  begin  
    sek := 0; 
    inc(min); 

background image

151 

 

  end; 
  Label00_00.Caption := IntToStr(min)+':'+IntToStr(sek); 
end; 

 

 

Rys. 10.9. Widok środowiska Lazarus – budowa aplikacji Stoper1

 

 
Komponenty graficzne opisane są w pliku tekstowym Unit1.pas. 
 
Kod aplikacji 

Stoper1 (formularza Form1) znajduje się w module Unit1.pas: 

 

unit Unit1; 
{$mode objfpc}{$H+} 
Interface 
uses
 Classes, SysUtils, FileUtil, LResources, Forms, 
Controls, Graphics, Dialogs, StdCtrls, ExtCtrls; 
 
type 
  { TForm1 
   TForm1 = class(TForm) 
   Label_00: TLabel; 
   Timer1: TTimer; 
   procedure Timer1Timer(Sender: TObject); 
   private     { private declarations } 
   public      { public declarations  } 

background image

152 

 

   min, sek: Integer
end; 
 
var  Form1: TForm1; 
implementation 
{ TForm1 
 
procedure TForm1.Timer1Timer(Sender: TObject); 
begin 
  inc(sek); // tu b

ę

dziemy co 1000ms 

  if sek >= 60 then 
    begin 
      sek := 0; 
      inc(min); 
    end; 
  Label_00.Caption := IntToStr(min)+':'+IntToStr(sek); 
end; 
 
initialization 
  {$I unit1.lrs} 
end. 

 
Plik główny aplikacji, czyli stoper1.lpr  ma postać: 
 

program project1; 
{$mode objfpc}{$H+} 
uses 
  {$IFDEF UNIX}{$IFDEF UseCThreads} 
  cthreads, 
  {$ENDIF}{$ENDIF} 
  Interfaces, // this includes the LCL widgetset 
  Forms, Unit1, LResources 
  {$IFDEF WINDOWS}{$R project1.rc}{$ENDIF} 
 
BEGIN 
  {$I stoper1.lrs} 
  Application.Initialize; 
  Application.CreateForm(TForm1, Form1); 
  Application.Run; 
END. 

 

 

background image

153 

 

Aplikacja Stoper3: 
 

  

 

Rys. 10.10. Widok formularza i aplikacji Stoper3

 

 
Zmienne  

sek

  i  

min 

 definiujemy jako składniki klasy Form1. 

TForm1 = class(TForm) 
… 
  public { Public declarations } 
    min, sek : Integer

  

Treść procedury dla przycisku 

Start: 

procedure TForm1.Button1Click(Sender: TObject); 
begin 
  sek:=0; min:=0;            //zerowanie zmiennych 
  Label1.Caption := '00:00'; //wyzerowanie stopera 
  Timer1.Enabled := True;    //

ą

czenie stopera 

end; 

 
Treść procedury dla przycisku 

Stop: 

procedure TForm1.Button2Click(Sender: TObject); 
begin 
  Timer1.Enabled := False; // zatrzymanie stopera  
end; 

 
Treść procedury dla przycisku 

Koniec: 

procedure TForm1.Button3Click(Sender: TObject); 
begin 
  Close; // zamkni

ę

cie aplikacji 

end; 

 
Treść procedury dla komponentu Timer1: 

//zliczanie czasu stopera3  

procedure TForm1.Timer1Timer(Sender: TObject); 
begin 

background image

154 

 

  //tu b

ę

dziemy co 1000ms 

  inc(sek);      //zlicza sekundy 
  if sek >= 60 then 
  Begin 
    sek := 0; 
    inc(min);    //zlicza minuty 
  end; 
 Label1.Caption := IntToStr(min)+ ':'+IntToStr(sek); 
end; 

 

10.7.

 

Aplikacja Kalkulator 

Zbudować czterodziałaniowy kalkulator arytmetyczny na liczbach całkowi-

tych (lub rzeczywistych). 

ZałoŜenia: 

 

dane (argumenty) będą wpisywane  w dwóch oknach tekstowych, 

 

wynik będzie umieszczany w (trzecim) oknie tekstowym,  

 

działania arytmetyczne + – * / będą wywoływane przyciskami TButton, 

 

dodatkowe dwa przyciski: 

C – wyczyści wynik;  Zamknij (lub Koniec) 

– zamknie aplikację. 

 

 

 

Rys. 10.11. Widok formularza i aplikacji kalkulatora 4-działaniowego

 

 

Wszystkim komponentom  naleŜy wpisać nazwy odpowiednie do ich funk-

cji. Trzeba tego dokonać w oknie 

Inspektora obiektów, w polu Name po klik-

nięciu wybranego obiektu. Przy okazji zmienić napisy na komponentach w po-
lach Caption oraz wielkość czcionki w polach Font (na 20 pkt.). 

 

background image

155 

 

Tabela 10.3. Ustawienia komponentów w aplikacji Kalkulator

 

Name 

Caption 

Component 

Label1_a 

Label1 

Label2_b 

Label2 

Label3_wyn 

Wynik 

Label3 

B_Dodaj 

Button1 

B_Odejm 

 

Button2 

B_Mnoz 

Button3 

B_Dziel 

Button4 

B_Czysc 

Button5 

B_Koniec 

Koniec 

Button6 

Panel1 

Kalkulator 

Panel1  

Name 

Text 

Component 

x1 

znak pusty 

Edit1 

x2 

znak pusty 

Edit2 

Wynik  

znak pusty 

Edit3 

 

Poprzez podwójne kliknięcie lewym przyciskiem myszy wygenerować pro-

cedury  dla  kaŜdego  przycisku.  Zmienne  dla  argumentów  zdefiniować  jako 
zmienne globalne modułu lub pola w klasie Form1. 

 

var  a, b, c : Integer; // zmienne modułu unit1.pas

 

public 
  a, b, c : Integer;   // pola klasy Form1. 
procedure TForm1.B_DodajClick(Sender: TObject); 
begin
 //zamiana tekstu w oknach edycji x1 i x2 na liczby 
  a:=strtoInt(x1.text);  
  b:=strtoInt(x2.text); 
  c:=a+b; //dodawanie liczb 
  wynik.text:=inttostr(c); //zamiana liczby c na tekst 
  Label3_wyn.caption:='suma';//zmienny komentarz wyniku 
end; 
procedure TForm1.B_OdejmClick(Sender: TObject); 
begin 
  a:=strtoint(x1.text);      
  b:=strtoint(x2.text); 
  c:=a-b; 
  wynik.text:=inttostr(c); 
  Label3_wyn.caption:= 'ró

Ŝ

nica'; 

end; 
procedure TForm1.B_MnozClick(Sender: TObject);
 
begin  
  a:=strtoint(x1.text); 

background image

156 

 

  b:=strtoint(x2.text); 
  c:=a*b; 
  wynik.text:=inttostr(c); 
  Label3_wynik.caption:='iloczyn'; 
end; 
procedure TForm1.B_DzielClick(Sender: TObject);
 
begin 
  a:=strtoint(x1.text); 
  b:=strtoint(x2.text); 
  if b<>0 then c:=a div b  else  c:=0; 
  if b<>0 then wynik.text:=inttostr(c) 
    else begin wynik.Text:='Error!'; 
            showmessage('Nie dziel przez zero !'); 
         end;       
  //wyswietlenie komunikatu w dodatkowym oknie 
  Label3_wynik.caption:='iloraz';  
end; 
procedure TForm1.B_czyscClick(Sender: TObject);
 
begin  
  x1.text:='';       //czyszczenie okna edycji  x1 
  x2.text:=''; 
  wynik.text:='';   //czyszczenie okna edycji wyniku 
  label3_wynik.caption:='wynik'; 
end; 
procedure TForm1.B_KoniecClick(Sender: TObject);
 
begin 
  Close;  //zamkni

ę

cie aplikacji 

end; 

 

10.8.

 

Aplikacja Równanie kwadratowe 

Zadanie  polega  na  obliczaniu  i  wyświetlaniu  pierwiastków  równania  kwa-

dratowego oraz rysowaniu wykresu odpowiadającej mu paraboli. ZałoŜenia: 

 

współczynniki ab i c równania będą podawane w oknach SpinEdit,  

 

wyniki (pierwiastki) będą wyświetlane w oknach edycji TEdit, 

 

komentarze pojawią się w etykietach TLabel,  

 

wykres zostanie sporządzony w komponencie TChart, 

 

zaprogramowana zostanie akcja na przycisk. 

RozwaŜone zostaną cztery przypadki: 

 

dwa pierwiastki rzeczywiste róŜne, 

 

jeden pierwiastek podwójny – widoczna jedna wartość w jednym oknie, 

 

brak pierwiastków – odpowiedni komunikat i brak okien, 

 

rozwiązanie równania liniowego, gdy a = 0. 

background image

157 

 

Formularz aplikacji i rozmieszczenie komponentów pokazano na rys. 10.12. 
 

 

Rys. 10.12. Widok formularza aplikacji do rozwiązywania równań kwadratowych

 

 
Wstawianie wykresu: 

1.

 

wstaw komponent TChart, 

2.

 

wykonaj szybkie podwójne kliknięcie lewym przyciskiem na wykresie. 
Pojawi się okno wstawiania 

linii do wykresów (Edit series – Chart1). 

Wybierz

  Add  (wstawianie  komponentu  ChartSeriesEdit),  czyli  dodaj 

jedną 

linię na wykresie

3.

 

sformatuj linię wykresu, uŜywając właściwości SeriesColor, Title itp. 

 

 

Rys. 10.13. Okno do wstawiania/usuwania linii z wykresu 

 

background image

158 

 

Kod modułu unit1, czyli rozwiązanie zadania: 
 

Unit unit1; 
  TForm1 = class(TForm) 
    Button1: TButton; //oblicz 
    Chart1: TChart; // wykres - siatka 
    wynikx1: TEdit; // okno wyniku pierwszego pierwiastka 
    wynikx2: TEdit; // okno wyniku drugiego pierwiastka 
    chart2LS: TLineSeries; //wykres – linia funkcji kw. 
    funckja: TLabel; // opis 
    lx1: TLabel;     // opis 
    lx2: TLabel; 
    lax2: TLabel; 
    lbx: TLabel; 
    lc: TLabel; 
    abox: TFloatSpinEdit; // współczynnik równania (a) 
    bbox: TFloatSpinEdit; // współczynnik równania (b) 
    cbox: TFloatSpinEdit; // współczynnik równania (c) 
    procedure Button1Click(Sender: TObject);   
      //procedura obliczania pierwiastków równania 
    private 
     { private declarations } 
    public 
     { public declarations } 
end; 
 
var Form1: TForm1; 
    implementation 
{$R *.lfm} 
{ TForm1 } 
 
procedure TForm1.Button1Click(Sender: TObject); 
const   
   MARGIN = 10; //definiuje margines 
var 
   a: Double;    // parametr a 
   b: Double;    // parametr b 
   c: Double;    // parametr c 
 
  x1, x2: Double; // x1 i x2 pierwiastki równania kwadr. 
  delta:  Double; // wyró

Ŝ

nik trójmianu kwadratowego 

  // zmienne potrzebne do p

ę

tli for i wykresu 

  x, y: Double
  i: Integer
  mlewy, mprawy: Longint; //marginesy 

background image

159 

 

 
begin 
  a := abox.Value; //dane ze SpeedButtons 
  b := bbox.Value; 
  c := cbox.Value; 
 
 if a<>0 then //sprawdzamy, czy a jest ró

Ŝ

ne od zera 

  begin 
  // piszemy posta

ć

 ogoln

ą

 funkcji 

  funckja.caption := FloatToStr(a)+'x^2+'+FloatToStr(b) 
  +'x+'+FloatToStr(c); 
  //obliczamy delt

ę

 ze wzoru: b^2 - 4*a*c 

  delta := b*b-(4*a*c); 
  x1 := 0;   
  x2 := 0; 
 
  if delta>0 then //je

Ŝ

eli delta>0, to 2 pierwiastki 

  begin 
   x1 := (-b+sqrt(delta))/(2*a);//obliczenie pierwiastków 
   x2 := (-b-sqrt(delta))/(2*a);  

 

   wynikx1.Text := FloatToStr(x1);  
   //wy

ś

wietlenie wyników 

   wynikx2.Text := FloatToStr(x2); 
   if x2>x1 then  //ustalenie marginesu wykresu 
      begin 
         mprawy := round(x1)+MARGIN; 
         mlewy  := round(x2)-MARGIN; 
      end 
      else   
      begin 
         mlewy  := round(x1)-MARGIN; 
         mprawy := round(x2)+MARGIN; 
      end; 
  end 
  else  
   if delta=0 then  //je

Ŝ

eli delta=0, to 1 pierwiastek 

    begin 
     x1 := -b/(2*a); // wzor na pierwiastek 
     wynikx1.Text := FloatToStr(x1);//wy

ś

wietlenie wyn. 

     wynikx2.Text := FloatToStr(x1); 
     mlewy  := round(x1-MARGIN);  
     mprawy := round(x1+MARGIN); //ustalenie marginesu 
    end 
  else  
   if delta<0 then // brak pierwiastków rzeczywistych 
    begin 

background image

160 

 

      wynikx1.Text := 'brak miejsc zerowych'; 
       //wyswietlenie wyników 
      wynikx2.Text := ''; 
      mlewy := -5; //ustalenie marginesu wykresu  
      mprawy := 5; 
     end; 
 end 
 
 else //funkcja liniowa 
  begin 
    x1 := 0; //zerowanie pierwiastków 
    x2 := 0; 
    funckja.caption := FloatToStr(b)+'x+'+FloatToStr(c); 
     //wy

ś

wietlenie funkcji liniowej 

    if c <> 0 then  
     // je

Ŝ

eli parametr c<>0, to: 

      begin 
        x1 := (-b)/c; 
        wynikx1.Text := FloatToStr(x1);  
        //wyswietlenie wyników 
        wynikx2.Text := ''; 
      end; 
     else //jezeli c=0, to brak 
      begin 
        x1 := 0; 
        wynikx1.Text := FloatToStr(x1); 
        wynikx1.Text := ''; //wy

ś

wietlenie wyników 

        wynikx2.Text := ''; 
      end; 
     mlewy  := round(x1)-MARGIN; //ustalenie marginesu 
     mprawy := round(x1)+MARGIN; 
  end; 
                
  chart2LS.Clear; // czyszczenie wykresu 
                  // rysowanie funkcji kwadratowej
  for i := mlewy to mprawy do 
    begin 
     x := i; 
     y := (a*x*x)+(b*x)+c; 
     chart2LS.AddXY(x, y); //rysowanie punktu na wykresie 
    end; 
 end; 
 
end. 

 

 

background image

161 

 

 

Rys. 10.14. Widok okna aplikacji rozwiązującej równanie kwadratowe

 

 

10.9.

 

Rysowanie figur geometrycznych 

 

Zbudujemy  aplikację,  która  pozwoli  rysować  figury  geometryczne.  Figury 

moŜna rysować w kilku komponentach, jednak najbardziej odpowiednim do te-
go celu jest komponent TShape. 

ZałoŜenie: rysowanie figur geometrycznych na róŜne sposoby. 
 
Rozwiązanie: 

a)

 

rysowanie figur w komponencie TImage, 

b)

 

rysowanie prostych figur w komponencie TShape, 

c)

 

rysowanie złoŜonych figur w komponencie TShape, 

 
Potrzebne będą komponenty: TRadioGroup x2, TImage, TShape x3, TPanel. 
 

 

background image

162 

 

 

Rys. 10.15. Widok formularza aplikacji do rysowania figur

 

 

ZłoŜone kształty (np. trójkąty o róŜnym połoŜeniu) moŜna uzyskać poprzez 

odpowiednie przesłanianie i połoŜenie komponentów Shape2, Shape3 i Panel: 

1)

 

komponent  Shape2  ustaw  jako  Deltoid,  rozmiar  (Height=100, 
Width=200), kształt: 

Shape=stsquaredDiamond, 

2)

 

komponent  Shape3  ustaw  jako  kwadrat:  rozmiar  (Height=100, 
Width=100), kształt: 

Shape=setsquare, 

3)

 

komponenty Shape2 i Shape3 rozmieść tak, aby Shape3 zakrywało pra-
wą połowę komponentu Shape2, 

4)

 

komponent Panel1: rozmiar (Height=50, Width=200), ustaw tak, by za-
krywał dolną połowę komponentów Shape2 i Shape3. 

 

 

 

background image

163 

 

 

Rys. 10.16. Widok aplikacji w trakcie wyświetlania figur

 

 
Kod modułu (programu): 
 

TForm1 = class(TForm) 
    Image1: TImage; 
    Panel1: TPanel; 
    RadioGroup1: TRadioGroup; 
    RadioGroup2: TRadioGroup; 
    Shape1: TShape; 
    Shape2: TShape; 

    Shape3: TShape;   //

konstruktor 

    procedure FormCreate(Sender: TObject); 
    procedure RadioGroup1Click(Sender: TObject); 
    procedure RadioGroup2Click(Sender: TObject); 
    private 
     { private declarations } 

    public    //

↓↓↓↓

punkty do rysowania trójk

ą

ta 

      trojkat: array [1..3] of TPoint; 
      x: Integer
end; 
 
var Form1: TForm1; 
  szer, wys: Integer
  kolor: Tcolor; 
 

background image

164 

 

implementation 
{ TForm1 } 
// rysowanie figur w komponentach  Image1 i Shape1 
procedure TForm1.RadioGroup1Click(Sender: TObject); 
begin
   //ustalenie wierzchołków trójk

ą

ta (dowolnie) 

  troj[1].X := 100;  troj[1].Y := 100;  
  troj[2].X := 100;  troj[2].Y :=   0; 
  troj[3].X :=   0;  troj[3].Y := 100; 
  Image1.Canvas.Brush.Color := clWhite;   
  //Image1.Canvas.ClipRect; 
  Image1.Canvas.FillRect(Canvas.ClipRect); //czyszczenie 
  Shape1.Canvas.Brush.Color:=clWhite; // na biało 
  Shape1.Canvas.ClipRect; 
  Shape1.Canvas.FillRect(Canvas.ClipRect); //czyszczenie 
 
  //wybór figury do narysowania w Image1 
  //rysowanie koła 
  if Radiogroup1.ItemIndex=0 then 
    Image1.Canvas.Ellipse(100,100,0,0); 
  //rysowanie kwadratu  
  if Radiogroup1.ItemIndex=1 then 
    Image1.Canvas.Rectangle(100,100,0,0); 
  //rysowanie prostok

ą

ta  

  if Radiogroup1.ItemIndex=2 then 
    Image1.Canvas.Rectangle(100,150,0,0); 
  //rysowanie trójk

ą

ta  

  if Radiogroup1.ItemIndex=3 then 
    Image1.Canvas.Polygon(troj);   
  //wybór figury do narysowania w Shape1 poprz Canvas 
  //rysowanie koła  
  if Radiogroup1.ItemIndex=0 then 
    Shape1.Canvas.ellipse(100,100,0,0); 
  //rysowanie kwadratu  
  if Radiogroup1.ItemIndex=1 then 
    Shape1.Canvas.Rectangle(100,100,0,0); 
  //rysowanie prostok

ą

ta  

  if Radiogroup1.ItemIndex=2 then 
    Shape1.Canvas.RoundRect(100,150,0,0,15,15); 
  //rysowanie trójk

ą

ta  

  if Radiogroup1.ItemIndex=3 then 
    Shape1.Canvas.Polygon(troj); 
end; 
 
procedure TForm1.FormCreate(Sender: TObject); 
begin
    // shape2.Width:=100; 
  wys := shape2.Height; 

background image

165 

 

  szer := shape2.Width; 
  Shape3.Brush.color := clDefault;  //kolor tla  
  kolor := Panel1.color; 
  // Shape3.Pen.color := clDefault;  
  // aby zaslaniajacy ksztalt nie byl widoczny 
  Panel1.visible := false; 
end; 
 
//rysowanie figur w  komponentach  Shape2 i Shape3 
procedure TForm1.RadioGroup2Click(Sender: TObject); 
begin 
  Shape2.Brush.Color := 123; //kolor br

ą

zowy 

  Shape3.visible := false; 
  Panel1.Visible := False; 
  //schowanie komponentu przyslaniaj

ą

cego Panel1 

  if Radiogroup2.ItemIndex=0 then 
    Shape2.shape := stCircle;   //rysowanie koła 
  if Radiogroup2.ItemIndex=1 then 
    Shape2.shape := stSquare;   //rysowanie kwadratu 
  if Radiogroup2.ItemIndex=2 then 
    Shape2.shape := stRoundSquare; 
   //rysowanie zaokr

ą

glonego kwadratu 

  if Radiogroup2.ItemIndex=3 then 
    begin 
     Shape2.shape := stRectangle; //rysowanie prostok

ą

ta 

    end; 
  if Radiogroup2.ItemIndex=4 then 
    Shape2.shape := stRoundRect; 
   //rysowanie  zaokr

ą

glonego prostok

ą

ta 

  if Radiogroup2.ItemIndex=5 then 
    Shape2.shape := stEllipse; 

//rysowanie elipsy 

  if Radiogroup2.ItemIndex=6 then 
    Shape2.shape := stDiamond; 

//rysowanie deltoidu 

  if Radiogroup2.ItemIndex=7 then 
   begin 
    Shape2.shape := stSquaredDiamond; 
    //rysowanie trojkata przez zasloniecie 
    //polowy rombu przez kwadrat (Shape3) 
    Shape3.Pen.Style := psClear;//przezroczyste pioro 
    //Shape3.Pen.Color := kolor; ; 
    Shape3.shape := stSquare; 
    Shape3.visible := true; 
   end; 
  if Radiogroup2.ItemIndex=8 then 
   begin 
     Shape2.shape := stSquaredDiamond; 

background image

166 

 

    //rysowanie trojkata przez zasloniecie 
    //polowy rombu przez Panel1 
    Panel1.Visible := True; 
  end; 
 end; 
end. 

 

10.10.

 

Tablice i komponent TListBox 

PokaŜemy  jak  stworzyć  aplikację,  której  celem  jest  praca  z  tablicą  jedno-

wymiarową o określonym rozmiarze. Zadania szczegółowe: 

 

generowanie danych losowych do tablicy (przycisk 

Losuj – Button2), 

 

wyświetlenie elementów tablicy (komponent 

Listbox1), 

 

wybór typu elementów tablicy (byte, Integer, float) (komponent Radio-
Group1), 

 

zmiana zakresu losowanych danych (ComboBox1), 

 

czyszczenie okna z danymi czyli komponentu listbox1 (przycisk – 

Wy-

czysc – Button3),  

 

dodanie  liczby  z  okna  typu  TEdit  do  okna  typu  TListBox  (przycisk  – 
Dodaj – Button1), 

 

pomnoŜenie elementów tablicy przez liczbę i wyświetlenie ich w innym 
oknie (komponent 

memo1, przycisk Mnoz – Button4), 

 

obliczenie  średniej  z  elementów  tablicy  (przycisk 

Srednia 

−−−−

  Button5 

+Label1), 

 

obliczenie  sumy  z  elementów  tablicy  (przycisk 

Srednia 

−−−−

  Button6 

+Label1), 

 

obliczenie  największego  elementu  tablicy  (przycisk 

Max 

−−−−

  Button7 

+Label1). 

 

Rozwiązanie: 

 
Rozmiar tablicy ustalono na 20. Typ elementów to Float, który moŜe prze- 

chować elementy typu 

Integer i Byte. Aktualny rozmiar tablicy N ustalono na 10 

(czyli będzie wyświetlana tablica 10-elementowa). Tablicę zadeklarowano jako 
zmienną globalną modułu (przed sekcją 

implementation). Zmienną x, do mno-

Ŝ

enia tablicy, teŜ zadeklarowano jako zmienną globalną. 

 

 

background image

167 

 

 

Rys. 10.17. Widok formularza wyświetlania tablicy w komponencie ListBox

 

 

 

Rys. 10.18. Widok aplikacji 

 wyświetlanie tablicy i operacje na tablicy

 

 

Kod modułu: 
 

unit Unit1; 
{$mode objfpc}{$H+} 

background image

168 

 

interface 
uses 
Classes, SysUtils, FileUtil, Forms, Controls, Graphics, 
Dialogs, StdCtrls,  ExtCtrls; 
 
type   { TForm1 } 
  TForm1 = class(TForm) 
    Button1: TButton;     Button2: TButton; 
    Button3: TButton;     Button4: TButton; 
    Button5: TButton;     Button6: TButton; 
    Button7: TButton; 
    ComboBox1: TComboBox; 
    Edit1: TEdit; 
    Label1: TLabel; 
    ListBox1: TListBox; 
    Memo1: TMemo; 
    RadioGroup1: TRadioGroup; 
    procedure Button1Click(Sender: TObject); 
    procedure Button2Click(Sender: TObject); 
    procedure Button3Click(Sender: TObject); 
    procedure Button4Click(Sender: TObject); 
    procedure Button5Click(Sender: TObject); 
    procedure Button6Click(Sender: TObject); 
    procedure Button7Click(Sender: TObject); 
    private 
    public 
end; 
 
var  Form1: TForm1; 
  A : Array[0..20] of float; 
  N : Integer = 9; 
  zakres : Integer = 10; 
  x : Integer
 
implementation 
{$R *.lfm} { TForm1 } 
 
procedure TForm1.Button1Click(Sender: TObject); 
begin 
  ListBox1.Items.Add(Edit1.Text) 
end; 
 
procedure TForm1.Button2Click(Sender: TObject); 
var
 i: Byte
begin 
  zakres:=StrtoInt(ComboBox1.Text); 

background image

169 

 

  for i:=0 to N do 
  begin 
  if RadioGroup1.ItemIndex=0 then 
   ListBox1.Items.Add(InttoStr(random(zakres))); 
  if RadioGroup1.ItemIndex=1 then 
   ListBox1.Items.Add(InttoStr(2*random(zakres)-zakres)); 
  if RadioGroup1.ItemIndex=2 then 
   ListBox1.Items.Add(FloattoStr(2*random-1)); 
  end; 
end; 
 
procedure TForm1.Button3Click(Sender: TObject); 
begin 
  Memo1.Text :=ListBox1.Items.GetText; 
  ListBox1.Items.Clear(); 
end; 
 
procedure TForm1.Button4Click(Sender: TObject); 
var
 i: Byte
begin 
  x := 1; 
  x := StrtoInt(Edit1.Text); 
  Memo1.Text := ''; 
  for i:=0 to N do 
   begin 
    ListBox1.ItemIndex:=i; 
    A[i] := StrtoFloat 
      (ListBox1.Items.Strings[ListBox1.ItemIndex]); 
    Memo1.Lines.Add(FloattoStr(A[i]*x)); 
   end; 
end; 
 
procedure TForm1.Button5Click(Sender: TObject); 
var
 i: Byte
  max: Double
begin 
  max := A[0]; 
  for  i:=0 to N do 
   if A[i]> max  then max := A[i]; 
  Label1.Caption := FloatToStr(max); 
end; 
 
procedure TForm1.Button6Click(Sender: TObject); 
var
 i: Byte
    s: Double
begin 

background image

170 

 

  s := 0; 
  for i:=0 to N do s := s+A[i]; 
    Label1.Caption := FloatToStr(s/(N+1)); 
end; 
 
procedure TForm1.Button7Click(Sender: TObject); 
var i: Byte
    s: Double
begin 
  s := 0; 
  for i:=0 to N do 
   begin 
    // ListBox1.ItemIndex := i; 
    // A[i] := StrtoFloat 
    // ListBox1.Items.Strings[ListBox1.ItemIndex]; 
    s := s+A[i]; 
   end; 
  Label1.Caption := FloatToStr(s); 
end; 
 
end. 

 

10.11.

 

Okna dialogowe 

Napiszemy aplikację, której celem jest praca z oknami dialogowymi. 
 
Zadania szczegółowe: 

 

pisanie tekstu w oknie tekstowym (w komponencie 

Memo1), 

 

zapisanie tekstu znajdującego się w Memo1 do pliku tekstowego 
(za pomocą przycisku 

Save – Button3 oraz komponentu SaveDialog1), 

 

odczytanie tekstu z pliku i wpisanie go do okna 

Memo1 (za pomocą 

przycisku 

Open – Button1 oraz komponentu OpenPictureDialog1), 

 

odczytanie rysunku i pokazanie go w oknie graficznym (za pomocą 
przycisku  

GraphOpen – Button2 oraz Image1 i OpenDialog1), 

 

zmiana koloru czcionki tekstu (za pomocą przycisku 

Color – Button4 

oraz komponentu 

ColorDialog1), 

 

zmiana formatu czcionki tekstu (za pomocą przycisku

 Font – Button6 

oraz komponentu 

FontDialog1). 

 

 

background image

171 

 

 

Rys. 10.19. Widok formularza aplikacji do edytowania tekstu 

i wykorzystania okien dialogowych 

 
Kod modułu: 
 

unit Unit1;  
{$mode objfpc}{$H+} 
interface 
uses
   Classes, SysUtils, FileUtil, Forms, Controls, 
Graphics, Dialogs, StdCtrls,  ExtCtrls, PopupNotifier, 
ExtDlgs, SynEdit, PrintersDlgs; 
 
type   { TForm1 } 
  TForm1 = class(TForm) 
    Button1: TButton;     Button2: TButton; 
    Button3: TButton;     Button4: TButton; 
    Button5: TButton;     Button6: TButton; 
    ColorDialog1: TColorDialog; 
    FontDialog1: TFontDialog; 
    Image1: TImage; 
    Memo1: TMemo;     Memo2: TMemo; 
    OpenDialog1: TOpenDialog; 
    OpenPictureDialog1: TOpenPictureDialog; 
    PrintDialog1: TPrintDialog; 
    SaveDialog1: TSaveDialog; 

background image

172 

 

    procedure Button1Click(Sender: TObject); 
    procedure Button2Click(Sender: TObject); 
    procedure Button3Click(Sender: TObject); 
    procedure Button4Click(Sender: TObject); 
    procedure Button5Click(Sender: TObject); 
    procedure Button6Click(Sender: TObject); 
    private 
    public 
     { public declarations } 
end; 
 
var   Form1: TForm1; 
implementation 
{$R *.lfm} { TForm1 } 
 
procedure TForm1.Button1Click(Sender: TObject); 
begin //uzycie OpenDialog 
  if OpenDialog1.Execute  then 
    Memo1.Lines.LoadFromFile(OpenDialog1.FileName); 
end; 
 
procedure TForm1.Button2Click(Sender: TObject); 
begin   //ustawienie filtru 
  OpenPictureDialog1.Filter:='*.bmp';  
  if OpenPictureDialog1.Execute then //załadowanie obrazu 
    Image1.Picture.LoadFromFile 
    (OpenPictureDialog1.FileName); 
end; 
 
procedure TForm1.Button3Click(Sender: TObject); 
begin 
  if SaveDialog1.Execute  then 
    Memo1.Lines.SaveToFile(SaveDialog1.FileName); 
end; 
 
procedure TForm1.Button4Click(Sender: TObject); 
begin 
  if colorDialog1.Execute then 
    Memo1.Color:=ColorDialog1.Color; 
end; 
 
procedure TForm1.Button5Click(Sender: TObject); 
begin 
  if PrintDialog1.Execute then Memo2.Text:='Drukuj'; 
  // PrintDialog1.Print:=Memo1.Lines.ToString; 
end; 

background image

173 

 

 
procedure
 TForm1.Button6Click(Sender: TObject); 
begin 
  if FontDialog1.Execute then 
    Memo1.Font:=FontDialog1.Font; 
end; 
 
end. 

 

10.12.

 

Tablice jednowymiarowe i komponent TStringGrid 

Napiszemy  aplikację,  do  pracy  z  tablicą  jednowymiarową,  przy  uŜyciu 

komponentu 

TSringGrid

 
Zadania szczegółowe: 

 

generowanie danych losowych do tablicy (przycisk 

Losowo-Button1); 

 

wyświetlenie elementów tablicy (komponent StringGrid1) 

 

obliczenie sumy z elementów tablicy (przycisk 

Suma – Button2 + La-

bel1) 

 

obliczenie średniej arytmetycznej elementów tablicy (przycisk 

Srednia 

– Button3 + Label2) 

 

obliczenie największego elementu tablicy (komponent LabelEdit1) 

 

zmiana koloru siatki (komponent CheckBox1) 

 

Rozwiązanie: 

Rozmiar tablicy ustalono na 10 (N = 9, indeksowanie od 0). Typ elementów 

to 

Integer.  Tablicę  zadeklarowano  jako  pole  klasy.  Tablicę  jednowymiarową 

moŜna wyświetlić w komponencie StringGrid (siatka) w poziomie (kolumnowo) 
lub  w  pionie  (wierszowo).  W  tej  aplikacji  tablica  będzie  wyświetlana  w  jednej 
kolumnie.  Jest  to  wynik  ustawienia  parametru  RowCount=11  (10+1)  -  licznik 
wierszy. Dodatkowa „jedynka” to liczba wierszy nagłówkowych (ustalonych pa-
rametrem FixedRows =1). 

Drugi  rozmiar  siatki  StringGrid1  to  parametr  ColCount=2  (1+1)  -  licznik 

kolumn. Dodatkowa „jedynka” to liczba kolumn nagłówkowych (ustalonych pa-
rametrem FixedCols=1). W ustalonych kolumnach i wierszach moŜna wstawiać 
np. podpisy kolumn/wierszy, numery kolumn/wierszy itp. 
 

 

background image

174 

 

 

 

Rys. 10.20. a) Widok formularza do generowania i wyświetlania tablicy, 

b) widok działającej aplikacji 

 

Kod modułu: 
 

unit Unit1;  
{$mode objfpc}{$H+} 
interface 
uses
  Classes, SysUtils, FileUtil, Forms, Controls, 
Graphics, Dialogs, StdCtrls, Grids, Buttons, ExtCtrls; 
const N=9; 
type
  { TForm1 } 
  TForm1 = class(TForm) 
    Button1: TButton;      
    Button2: TButton; 
    Button3: TButton; 
    CheckBox1: TCheckBox; 
    Label1: TLabel;      
    Label2: TLabel; 
    LabeledEdit1: TLabeledEdit; 
    StringGrid1: TStringGrid; 
    procedure Button1Click(Sender: TObject); 
    procedure Button2Click(Sender: TObject); 
    procedure Button3Click(Sender: TObject); 
    procedure CheckBox1Change(Sender: TObject); 
    procedure FormCreate(Sender: TObject); 

background image

175 

 

    procedure LabeledEdit1Click(Sender: TObject); 
    public 
      wektor:array[0..N]of integer; 
end; 
 
var  Form1: TForm1;  
  wektor1 : array [0..N] of Integer; 
implementation 
{$R *.lfm} { TForm1 } 
 
procedure TForm1.FormCreate(Sender: TObject); 
var
 i : Byte
begin 
  for i:=0 to N do       //numerowanie wierszy 
    StringGrid1.Cells[0,i+1] := Inttostr(i+1); 
    // Cels[k,w] k=kolumny w=wiersze 
end
 
procedure TForm1.Button1Click(Sender: TObject); 
var
  i : Integer
begin 
  Randomize; 
  for i := 0 to N do  
  wektor[i] := random(21)-10;   //wpis do wektora 
       //przepisanie danych tablicy  do siatki 
  for i:=0 to N do 
    StringGrid1.Cells[1,i+1]:=Inttostr(wektor[i]); 
end; 
 
procedure TForm1.Button2Click(Sender: TObject); 
var
  suma, i : Integer
begin 
  suma:=0; 
  for i:=0 to N do 
    begin 
      wektor[i]:=StrToInt(StringGrid1.Cells[1,i+1]); 
      suma:=suma+wektor[i]; 
    end; 
  Label1.Caption:='suma='+IntToStr(suma); 
end
 
procedure TForm1.Button3Click(Sender: TObject); 
var
  suma, i : Integer
begin 
  suma := 0; 
  for i := 0 to N do 

background image

176 

 

  begin 
    wektor[i] := StrToInt(StringGrid1.Cells[1,i+1]); 
    suma := suma+wektor[i]; 
  end; 
  Label2.Caption:='srednia='+FloatToStr(suma/(N+1)); 
 end
 
procedure TForm1.LabeledEdit1Click(Sender: TObject); 
var
  max, i : Integer
begin 
  max := wektor[0]; 
  for i := 0 to N do    
  begin 
    wektor[i] := StrToInt(StringGrid1.Cells[1,i+1]); 
    if  wektor[i] > max then max := wektor[i]; 
  end; 
  LabeledEdit1.text:=IntToStr(max); 
end
 
procedure TForm1.CheckBox1Change(Sender: TObject); 
begin 
  if CheckBox1.Checked := True then 
        StringGrid1.Color := clYellow 
  else  StringGrid1.Color := clDefault; 
end
 
end. 
 

10.13.

 

Tablice dwuwymiarowe i komponent TStringGrid 

Napiszemy aplikację, do pracy z tablicą dwuwymiarową, przy uŜyciu kom-

ponentu 

TSringGrid

 
Zadania szczegółowe: 

 

generowanie danych losowych do tablicy (przycisk 

Losowo:Button1), 

 

wyświetlenie elementów tablicy (komponent StringGrid1), 

 

obliczenie sumy elementów tablicy (przycisk 

Suma:Button2 +Label1), 

 

obliczenie średniej arytmetycznej elementów tablicy (przycisk 

Srednia: 

Button3

 + Label2), 

 

obliczenie największego elementu tablicy (komponent LabelEdit1), 

 

zamykanie aplikacji (przycisk 

Koniec: Button4). 

 

Rozwiązanie: 

background image

177 

 

Rozmiar tablicy ustalono na 10x10 (rM=10). Typ elementów 

 na 

Integer

Tablicę zadeklarowano jako pole klasy. Aktualny rozmiar tablicy N = 4 ustalono 
w  konstruktorze  (

FormCreate).  Ustawiono  parametr  RowCount  =  11  (10+1)  - 

licznik wierszy oraz parametr ColCount = 11 (10+1) - licznik kolumn. Kolumna 
i wiersz nagłówkowy: parametr FixedCols = 1  oraz  FixedRows = 1. 

 

 

Rys. 10.21. Widok formularza aplikacji do generowania i wyświetlania macierzy

 

 

Kod modułu: 
 

unit Unit1; 
{$mode objfpc}{$H+} 
interface 
uses
  Classes, SysUtils, FileUtil, Forms, Controls, 
Graphics, Dialogs, StdCtrls,Grids, Buttons, ExtCtrls; 
 
const rM = 10; 
type
   { TForm1 } 
  TForm1 = class(TForm) 
    Button1: TButton;   Button2: TButton; 
    Button3: TButton;   Button4: TButton; 
    Label1: TLabel;     Label2: TLabel; 
    LabeledEdit1: TLabeledEdit; 
    StringGrid1: TStringGrid; 
    procedure Button1Click(Sender: TObject); 
    procedure Button2Click(Sender: TObject); 
    procedure Button3Click(Sender: TObject); 
    procedure Button4Click(Sender: TObject); 
    procedure FormCreate(Sender: TObject); 
    procedure LabeledEdit1Change(Sender: TObject); 

background image

178 

 

    procedure LabeledEdit1Click(Sender: TObject); 
 
    private 
    { private declarations } 
    public 
      macierz : array [1..rM,1..rM] of Integer
      N : Byte; //rozmiar siatki 
end; 
 
var  Form1: TForm1; 
implementation 
{$R *.lfm} 
{ TForm1 } 
 
procedure TForm1.FormCreate(Sender : TObject); 
var
 i : Byte
begin 
  N := 4; 
  for i := 1 to N do         //numerowanie wierszy 
    StringGrid1.Cells[0,i] := IntToStr(i); 
  for i := 1 to N do         //numerowanie kolumn 
    StringGrid1.Cells[i,0] := IntToStr(i); 
   // Cels[k,w] k=kolumny w=wiersze 
end; 
 
procedure TForm1.Button1Click(Sender : TObject); 
var
  i, j : Integer
begin 
  Randomize; 
  for i:=1 to N do 
    for j:=1 to N do  
      macierz[i,j] := random(21)-10; //wpis do wektora 
    // przepisanie danych z macierzy do siatki 
  for i:=1 to N do 
    for j:=1 to N do 
      StringGrid1.Cells[j,i] := IntToStr(macierz[i,j]); 
end
 
procedure TForm1.Button2Click(Sender : TObject); 
var
  suma, i, j : Integer
begin  
  suma := 0; 
  for i := 1 to N do 
   for j := 1 to N do 
     begin 
        macierz[i,j] := StrToInt(StringGrid1.Cells[j,i]); 

background image

179 

 

        suma := suma+macierz[i,j]; 
     end; 
  Label1.Caption := 'suma=' + IntToStr(suma); 
end
 
procedure TForm1.Button3Click(Sender : TObject); 
var
  suma, i, j : Integer
begin 
  suma := 0; 
  for i := 1 to N do 
   for j := 1 to N do 
    begin 
      macierz[i,j] := StrToInt(StringGrid1.Cells[j,i]); 
      suma := suma+macierz[i,j]; 
    end; 
  Label2.Caption := 'srednia='+FloatToStr(suma/(N*N)); 
end
 
procedure  TForm1.LabeledEdit1Click(Sender : TObject); 
var
  max, i, j : Integer
begin 
  max := macierz[1,1]; 
  for i := 1 to N do 
   for j := 1 to N do 
     begin 
       macierz[i,j]:=StrToInt(StringGrid1.Cells[j,i]); 
       if macierz[i,j]> max then 
          begin 

      max := macierz[i,j]; 

            StringGrid1.GridLineColor := clYellow; 
          end 
       else 
          StringGrid1.GridLineColor := clGray; 
    end; 
  LabeledEdit1.text := IntToStr(max); 
end
 
procedure TForm1.Button4Click(Sender: TObject); 
begin 
  Close; 
end; 
 
end. 

 

 

background image

180 

 

10.14.

 

Zadania 

Zad. 10.1. Zbudować  aplikację  Silnia,  która  będzie  obliczać  wartość  silni  dla 

podanej liczby naturalnej (uŜyć komponentów Button, EditBox i Label). 

Zad. 10.2. Zbudować aplikację Licznik, która będzie zliczać do przodu kliknięcia 

na  przycisku1  oraz  zliczać  do  tyłu  kliknięcia  na  przycisku2.  W  oknie 
edycyjnym ma być moŜliwość ustalenia krotności zliczania (od 1 do 10). 

Zad. 10.3. Zbudować aplikację Potęgowanie, która będzie obliczać wartość po-

tęgi  danej  liczby:  a)  wersja  podstawowa,  czyli  tylko  potęga  całkowita 
dodatnia; b) potęga całkowita ujemna; c) potęga rzeczywista. 

Zad. 10.4. Zbudować aplikację Stoper

a)

 

która wyświetla róŜnicę czasu aktualnego i czasu startu aplikacji wyko-
rzystując funkcję 

Time lub  Now.  Program  ma  posiadać  dwa  przyciski: 

START/STOP oraz RESET.  

b)

 

po  10  sekundach  od  startu  stopera  cyfry  zmieniają  stan  co  200ms  za-
miast co 1 sekundę, a nagłówek programu zmienia się na „Stoper przy-
spieszył”. 

c)

 

w  oknie  głównym  pokazuje  aktualny  czas  (wskazówka:  przydatna  bę-
dzie funkcja 

Now (lub funkcja Time) oraz TimeToStr)

d)

 

po 10 sekundach od startu stopera na płótnie formy ma pojawiać się ko-
ło  z  czarnym  brzegiem  i  wnętrzem  migającym  na  czerwono-czarno,  co 
sekundę.  UŜyć  właściwości 

Canvas,  najlepiej  dla  komponentu  Image

ewentualnie dla głównej formy programu. 

e)

 

wielkość  cyfr  zmienia  się  stosownie  do  zmiennych  rozmiarów  okna 
aplikacji. Wskazówki:  aby  okno  mogło  zmieniać  rozmiary,  trzeba  wła-
ś

ciwość  BorderStyle  ustawić  na  bsSizeable.  UŜyć  w  programie  właści-

wości ClientHeight, ClientWidth. 

Zad. 10.5. Zbudować kalkulator do obliczeń na liczbach rzeczywistych. 

Zad. 10.6. Zbudować kalkulator do obliczeń na liczbach zespolonych. 

Zad. 10.7. Zbudować kalkulator do obliczeń na macierzach kwadratowych. 

Zad. 10.8. Zbudować aplikację dokładnego dodawania duŜych liczb naturalnych.  

Zad. 10.9. Zbudować aplikację, która będzie prostym edytorem tekstu. 

Zad. 10.10.  Zbudować  aplikację,  która  będzie  rysować  róŜne  figury:  trójkąt, 

kwadrat,  prostokąt,  trapez,  romb,  równoległobok,  sześciokąt.  Rozmiary 
figur podawane mają być w oknach typu TextBox. Aplikacja ma liczyć 
obwód i pole figury. 

Zad. 10.11. Zbudować aplikację do konwersji liczb na róŜne systemy liczbowe 

(dziesiętny, binarny, szesnastkowy, ósemkowy itp.). 

background image

181 

 

LITERATURA 

 

[1]

 

Cantu M.: Delphi 7. Praktyka programowania. Mikom, Warszawa, 2004. 

[2]

 

Gierliński M.: Pascal – nawet Ty moŜesz programować. Wydawnictwo Edi-
tion 2000, Kraków, 1998. 

[3]

 

Kierzkowski  A.:  Turbo  Pascal.  Ćwiczenia  praktyczne.  Helion,  Gliwice, 
2000. 

[4]

 

Kott Ryszard K.: Programowanie w języku Pascal. WNT, Warszawa, 1988. 

[5]

 

Marciniak A.: Object Pascal – język programowania w środowisku Borland 
Delphi 2.0
. Nakom, Poznań, 1997. 

[6]

 

Porębski  W.:  Pascal.  Wprowadzenie  do  programowania.  Komputerowa 
Oficyna Wydawnicza „HELP”, Michałowice pod Warszawą, 1999. 

[7]

 

Stephens R.: Algorytmy i struktury danych z przykładami w Delphi. Helion, 
Gliwice, 2000. 

[8]

 

Struzińska-Walczak A., Walczak K.: Nauka programowania dla … juŜ nie 
całkiem pocz
ątkujących. Wydawnictwo W&W, Warszawa, 2004. 

[9]

 

Struzińska-Walczak  A.,  Walczak  K.:  Nauka  programowania  w  systemie 
Delphi
. Wydawnictwo W&W, Warszawa, 2004.

 

[10]

 

Strzałkowski K.: Podstawy Delphi. Wydawnictwo Stachurski, Kielce, 2000. 

 

[11] 

http://wazniak.mimuw.edu.pl/index.php?title=Wst%C4%99p_do_progra  

            mowania 

[12] 

http://wazniak.mimuw.edu.pl/index.php?title=Paradygmaty_progra 
mowania 

[13] 

http://wazniak.mimuw.edu.pl/index.php?title=Matematyka_dyskretna_1 

[14] 

http://wiki.freepascal.org/Main_Page 

[15] 

http://wiki.freepascal.org/Object_Pascal_Tutorial 

[16] 

http://www.tutorialspoint.com/pascal/index.htm 

[17] 

http://turbopascal.helion.pl 

[18] 

http://www.pascal.vj.e.pl 

[19] 

http://edu.i-lo.tarnow.pl/inf/utils/010_2010/index.php 

[20] 

http://javablock.sourceforge.net/ 

[21] 

http://www.pm.waw.pl/~marwoj/javablock/javablock.pdf 

[22] 

http://lobrzozow.internetdsl.pl/materialy/ti/Ti2.htm