Stałe

Adam Boduch

const - słowo kluczowe języka Delphi

Podobnie jak Zmienne, stałe również służą do przechowywania jakichś danych podczas działania aplikacji. Istnieje jednak pomiędzy nimi istotna różnica. Stałe, jak sama nazwa wskazuje, nie mogą podlegać modyfikacji podczas działania programu. Czyli wartość stałych jest określana już podczas pisania programu:

program varConst;

const
  Stala1 = 'Oto jest stała...';

begin

end.

Stałe, w odróżnieniu od zmiennych, deklarujemy z użyciem słowa kluczowego const (od angielskiego słowa constant - stała). Jak widać, nie musimy deklarować typu stałej. Może on być ustalany na podstawie wartości jaką ma przechowywać.

const
  Stala2 = 12;

W ten sposób stała liczbowa, znakowa bądź zbiór może być typu porządkowego i może zostać użyta w instrukcji wielokrotnego wyboru case ... of ...

Programista może samodzielnie określić typ:

program varConst;

const
  Stala1 = 'Oto jest stała...';
  Stala2 : Byte = 12;

begin

end.

Delphi stara się ustalić jak najmniejszy zakres dla stałej. Patrz Tabela 1 i 2.

Tabela 1. Automatycznie określane typy stało liczbowe.

WartośćTyp
od -2^63 do - 2.147.483.649Int64
od -2.147.483.648 do -32.769Longint
od -32.768 do -129Smallint
od -128 do 127Shortint
od 128 do 255Byte
od 256 do 65.535Word
od 65.536 do 2.147.483.647Integer
od 2.147.483.648 do 4.294.967.295Cardinal
od 4.294.967.296 do 2^63-1Int64

Tabela 2. Inne automatycznie określane typy dla stałych

WartośćTyp
rzeczywistaExtended
stała łańcuchowa o dł. 1Char
stała łańcuchowa o dł. > 1 ∧ < 256ShortString
stała łańcuchowa o dł. > 255String
zbioryzakres wynika bezpośrednio z ich postaci
const
  x = 5;
  y = Pred(x);  // o ile x nie mam zadeklarowanego typu (jest ordynarne)
  z = y + 8;  // o ile y nie mam zadeklarowanego typu (jest ordynarne)
  q: Byte = Succ(x);
  a: Byte = Abs(q); //błąd

var
  w: Integer;
begin
  case w of
    x: {wybrano x};
    y: {wybrano y};
    z: {wybrano z};
  end;
end.

Stałą można definiować za pomocą wyrażenia, pod warunkiem, ze jest ono stałe.

Możliwe jest także użycie tzw. rzutowania, co w praktyce wiąże się z taką deklaracją:

const
  Int = Word(342);

W tym przypadku stała Int będzie typu Word o wartości 342. W praktyce jednak takie rzutowanie daje te same rezultaty co sposób określenia typu przedstawiony wcześniej.

W razie próby przypisania jakiejś wartości stałej, przykładowo:

begin
  Stala1 := 'Inna wartość';
end.

Delphi uzna to za błąd i wyświetli podpowiedź: [Error] varConst.dpr(8): Left side cannot be assigned to.

Przed chwilą wspomniałem, iż wartości stałych nie podlegają modyfikacji. Istnieje jednak mała ?sztuczka?, która pozwala na zmianę wartości, a polega na ustawieniu odpowiedniej dyrektywy kompilatora.

Dyrektywy kompilatora wyglądają jak zwykłe komentarze. Są one poprzedzane znakiem dolara ($) i umożliwiają zmianę opcji kompilacji. Przykładowo, dyrektywa {$APPTYPE CONSOLE} określa, że aplikacja będzie uruchamiana w oknie konsoli. Kompilator napotykając na taką dyrektywę modyfikuje sposób kompilacji kodu źródłowego.
Umieszczenie w kodzie dyrektywy {$J+} umożliwia modyfikację wartości stałych:

const
  {$J+} // wszystkie stałe jakie kompilator napotka od tej pory będą "wartościami".
  Stala1 : String = 'Początkowa wartość';
  {$J-} // wszystko wraca do normy.
begin
  Stala1 := 'Końcowa wartość';
end.

Pochodzi to z czasów świetności Turbo Psacal'a i Delphi 2, w których "stałe" dało się modyfikować tak jak zwykłe zmienne bez użycia dyrektyw. Dlaczego więc projektanci z Borland rozróżnili wtedy "dwa rodzaje" zmiennych? Proponuję spojrzeć na poniższy kod:

procedure Rekurencja;
const
  {$J+} i: Byte = 0; {$J-}
begin
  if i > 5 then Exit;
  Inc(i);
  WriteLn(i);
  Rekurencja;
end;

begin
  Rekurencja;
  Rekurencja;
  Rekurencja;
  ReadLn;
end.

Efekt jest bardzo zabawny. Wygląda na to, ze nasza stała nie jest tak do końca zwykłą zmienną. Zauważamy od razu dwie osobliwości. Pierwsza to to, że i jest wspólna dla wszystkich wywołań procedury (program powinie się zapętlić ze względu na to, że dla każdego wywołania wartość i wynosi 0, co najwyżej 1). Druga to to, że zachowuje swoją wartość mimo zwolnienia z zasobów procedury Rekurencja (biorąc pod uwagę pierwszą osobliwość powinniśmy zobaczyć na ekranie 3x wydruk od 0 do 6).

Ale przechodząc do sedna sprawy. I to nie stała. NIE NALEŻY TEGO NIGDY MYLIĆ. Jest to tzw. zmienna inicjowana. Jest nazywana stałą jedynie ze względy na słowo kluczowe.

Jest możliwość deklarowania stałych tablicowych i rekordowych.

const
  //Zwykła stała tablica
  arr: array [1..3] of String =
  (
    'linia #1',
    'linia #2',
    'linia #3'
  );

  //Zwykły stały rekord
  rec: record
    lAnanasow: Integer;
    lWinogron: Integer;
    lJablek: Integer;
    kupujacy: String;
  end =
  (
    lAnanasow: 5;
    lWinogron: 20;
    lJablek: 3;
    kupujacy: 'Pan Kowalski'
  );

  //stała tablica rekordów
  arrRec: array [1..4] of record
    liczba: Integer;
    ciag: String;
  end =
  (
    (
      liczba: 5;
      ciag: 'abc';
    ),
    (
      liczba: 10; // rekor nie musi być deklarowany w całości
    ),
    (), // rekord nie musi być deklarowany w ogóle
    (), // błąd [,] oddziela kolejne elementy
  );

Słowo kluczowe const umieszczone w nagłówku procedury lub funkcji oznacza przekazywanie parametru przez wartość. Więcej informacji na temat przekazywania parametrów, znajdziesz w artykule Procedury i funkcje.

Zobacz też:

2 komentarzy

Hmm... a co mozna jeszcze napisac o stalych tak aby byla to "nowosc"? :)
I uzycie Unicodu nie jest bledem aczkolwiek moze warto by bylo wsopmniec ze jedynie nowsze wersje.

Wszystko pięknie ładnie... w sumie nowości to tu nie ma, ale początkującym się przyda. Natomiast użycie polskich liter w nazwach stałych jest błędem, a nawet jeśli jakieś nowsze Delphi to puści to "Nie jest to jednak często spotykany sposób pracy, można więc potraktować to jako ciekawostkę. " :)