Согласие на обработку персональных данных.

21.07.2019
Новые видео и статья То, что вы не знали о константах.

01.07.2019
Новый выпуск журнала Автоматизация и программирование.

26.06.2019
Новые видео и статья Функция Eof.

19.06.2019
Новая задача Генератор наборов символов.

08.04.2019
Новый выпуск журнала Автоматизация и программирование.



Раздел: Как стать программистом / Секреты программирования /

То, что вы не знали о константах

Я уже не раз говорил о пользе применения констант, но решил посвятить этому вопросу отдельную статью, поскольку многие начинающие вообще не уделяют внимания константам - ну есть они и есть. А между тем...

1. Виды констант

В Lazarus/FreePascal константы бывают следующих видов:

  1. Обычные константы.
  2. Типизированные константы (не уверен, что начинающие программисты на Паскале об этом слышали, а вот для тех, кто начал с С/С++ - это самые что ни на есть обычные константы).
  3. Строка ресурса (а вот об этом, я почти уверен, вы даже и не слышали).

2. Объявление констант

FreePascal, как и Турбо Паскаль, поддерживает обычные и типизированные константы. Константы объявляются в разделе констант, в блоке объявления модуля, программы или класса, функции или процедуры.

Пример:

program myprog;

//Раздел констант в блоке объявления программы
const X = 100;

procedure MyProc;
//Раздел констант в блоке объявления процедуры
const Y = 50;
begin
end;

var Z : WORD;

//****************************************************************
// ОСНОВНАЯ ПРОГРАММА
//****************************************************************
begin
  Z := X;
  Z := Y;   //!!! Так нельзя, потому что Y - локальная константа
  WriteLn('The end. Press ENTER...');
  ReadLn;
end.

Обратите внимание на то, что при объявлении констант (как и при объявлении других сущностей), имеет значение область видимости. Поэтому в описанном выше примере компилятор выдаст ошибку (Y - это локальная константа, которая видна только внутри процедуры MyProc, а в программе эту константу компилятор “не увидит”).

3. Обычные константы

Объявления обычных констант создаются с использованием имени идентификатора, за которым следует знак “=”, а затем необязательное выражение, состоящее из допустимых комбинаций чисел, символов, логических значений или перечисляемых значений. Примерно так:

const Идентификатор = Выражение ДирективаПодсказки;

То есть объявлению константы (или раздела констант) предшествует ключевое слово const. Затем идёт Идентификатор - имя константы. А затем - Выражение, которое связывается с указанным Идентификатором. Например:

const X = 100;

Здесь Х - это Идентификатор (имя константы), а 100 - это Выражение. И теперь можно использовать эту константу, например, так:

WriteLn(X);   //Выведет 100
WriteLn(100); //Выведет 100

То есть вместо обычного числа или выражения можно использовать константу, и две процедуры WriteLn в примере выше выведут одинаковое число.

Компилятор должен иметь возможность вычислять выражение в объявлении константы во время компиляции. Это означает, что большинство функций библиотеки времени выполнения нельзя использовать в объявлении константы.

Операторы, такие как +, -, *, /, not, and, or, div, mod, ord, chr, sizeof, pi, int, trunc, round, frac, odd могут быть использованы.

Например:

const X = 100;
      XX = X * X;
      SIZE_X = SizeOf(X);

А вообще Выражение - это может быть любое допустимое выражение в Паскале, с учётом того, что оно может быть вычислено во время компиляции. Например, такой код не прокатит:

const XX = X * X;
      X = 100;

Потому что выражение XX = X * X НЕВОЗМОЖНО вычислить во время компиляции, потому как константа Х у нас объявлена ПОСЛЕ константы ХХ.

Что такое ДирективаПодсказки сегодня рассказывать не буду, потому как непосредственно к константам она отношения не имеет. Но обязательно расскажу в следующий раз.

В Паскале могут быть объявлены только константы следующих типов:

Следующие примеры являются правильными объявлениями констант:

const e = 2.7182818;                //Константа вещественного типа
      a = 2;                        //Целочисленная константа
      c = '4';                      //Символьная константа
      s = 'This is a string';       //Строковая константа
      sc = chr(32);                 //Символьная константа
      ls = SizeOf(Longint);         //Целочисленная константа
      P = NIL;                      //Константа-указатель
      SS = [1, 2];                  //Множество

Присваивание значения обычной константе не допускается. Таким образом, учитывая предыдущее объявление, следующий код приведет к ошибке компилятора:

s = 'string';

Кроме того, для строковых констант тип строки зависит от некоторых параметров компилятора. Если требуется определенный тип, следует использовать типизированную константу, как описано в следующем разделе.

4. Типизированные константы

Иногда необходимо указать тип константы, например для констант сложных (комплексных) структур, или когда значение константы надо изменить во время выполнения программы. Типизированные константы можно использовать для хранения массивов, записей, процедур...

Определение типизированных констант довольно простое:

const Идентификатор : Тип = Значение;

Пример:

const TC : WORD = 200; //Типизированная константа

А потом в программе можно сделать так:

TC := 500; //Значение типизированной константы можно изменять !

То есть в отличие от обычных констант, типизированным константам может быть присвоено значение во время выполнения. Это старая концепция Turbo Pascal, которая в Lazarus/FreePascal была заменена поддержкой инициализированных переменных.

Поддержка присваивания значений типизированным константам управляется директивой {$J}, то есть она (поддержка) может быть отключена. Однако она включена по умолчанию (для совместимости с Turbo Pascal). Инициализированные переменные всегда разрешены.

ПРИМЕЧАНИЕ: Следует подчеркнуть, что типизированные константы инициализируются автоматически при запуске программы. Это также верно для локальных типизированных констант и инициализированных переменных. Локальные типизированные константы также инициализируются при запуске программы. Если их значение было изменено во время предыдущих вызовов функции, они сохранят измененное значение, то есть они не инициализируются при каждом вызове функции.

ВАЖНО!
Типизированные константы, в отличие от инициализированных переменных, можно объявлять локально в подпрограммах. Пример:

function MyFunc(n : byte) : byte;
const Y = 50;   //Так можно
var IP = 100;   //А так нельзя !!!
begin
  Result := n + Y;
end;

4.1. Константы-массивы

Константу-массив можно объявить, заключив значения элементов массива в скобки и разделив их запятыми. Значениями элементов массива могут быть только константные выражения. Пример:

//Константа - одномерный массив
const M : array[0..7] of WORD = (0, 1, 2, 3, 4, 5, 6, 7);   

//Константа - двумерный массив
const MM : array[1..2, 1..3] of WORD = ((1, 2, 3),
                                        (4, 5, 6));

С одномерным массивом, думаю, вопросов не возникнет. А двумерный массив-константа, созданный в примере, будет содержать такие значения:

MM[1, 1] = 1;
MM[1, 2] = 2;
MM[1, 3] = 3;
MM[2, 1] = 4;
MM[2, 2] = 5;
MM[2, 3] = 6;

Теперь и с этим, надеюсь, всё понятно.

4.2. Константы-записи

Чтобы определить константу-запись, надо определить значение каждого поля так:

ИмяПоля : значение;

Присваивания значений полям разделяются точками с запятой (;). Значения должны быть константными выражениями. Поля должны быть перечислены в том же порядке, в каком они следуют в объявлении записи. Значение для поля-тэга (если такое присутствует) должно быть установлено. Если запись имеет вариантную часть, то значение может быть установлено только для варианта, определенного полем-тэгом.

Пример:

//Константа-запись (объявление записи)
type TKvadrat = record
  x1 : WORD;
  y1 : WORD;
  x2 : WORD;
  y2 : WORD;
end;

//Константа-запись (заполнение записи)
const Kvadrat : TKvadrat = (x1 : 0; y1 : 0; x2 : 150; y2 : 100);

4.3. Процедурные константы

Процедурная константа должна содержать идентификатор процедуры или функции, который совместим с типом константы. Пример:

function MyFunc(n : byte) : byte;
//Раздел констант в блоке объявления подпрограммы
const Y = 50;
begin
  Result := n + Y;
end;

type TFuncType = function(n : byte) : byte;

const MyFunction : TFuncType = MyFunc;

Теперь вы можете использовать процедурную константу MyFunction в программе вместо функции MyFunc:

WriteLn(MyFunction(5));

Зачем вообще нужны такие извращения? Поверьте на слово - иногда это удобно. Но это отдельная тема - как-нибудь расскажу подробнее.

ВНИМАНИЕ!
Описанный выше пример будет работать только в режиме совместимости с Делфи, установить который можно с помощью соответствующей директивы.

5. Строковые ресурсы

Статья получилась более чем длинной, но не волнуйтесь - это раздел последний.

Особый вид блока объявления констант - это блок строковых ресурсов - Resourcestring. Объявления этого блока очень похожи на объявления строковых констант: строки ресурсов действуют как строки-константы, но они могут быть локализованы с помощью набора специальных процедур модуля objpas. Блок объявления строки ресурса разрешен только в режимах Delphi или Objfpc.

Ниже приведен пример определения строковых ресурсов:

resourcestring
  FileMenu = '&File...';
  EditMenu = '&Edit...';

Все строковые константы, определенные в разделе resourcestring, хранятся в специальных таблицах. Со строками в этих таблицах можно работать во время выполнения некоторых специальных механизмов в модуле objpas.

Семантически строки действуют как обычные константы. Этим строкам не разрешается присваивать значения (за исключением специальных механизмов модуля objpas). Однако они могут использоваться в присваивании или выражениях как обычные строковые константы. Основное использование раздела resourcestring заключается в обеспечении простых средств интернационализации.

Более подробно с темой resourcestrings можно ознакомиться в руководстве программиста, а также в описании модуля objpas. Возможно, я когда-нибудь ещё к этому вернусь.

ПРИМЕЧАНИЕ: обратите внимание, что строка ресурса, которая задана как выражение, не изменится, если части выражения будут изменены:

resourcestring
  Part1 = 'First part of a long string.';
  Part2 = 'Second part of a long string.';
  Sentence = Part1 + ' ' + Part2;

Если процедуры локализации переводят Part1 и Part2, то константа Sentence не будет переведена автоматически: она имеет отдельную запись в таблицах строк ресурсов и должна быть переведена отдельно.

Вышеуказанная конструкция просто говорит, что начальное значение Sentence равняется Part1 + ' ' + Part2.

ВНИМАНИЕ!
Мой компилятор отказался компилировать Sentence = Part1 + ' ' + Part2;, сообщив, что это неправильное выражение. Возможно, это обычный глюк FreePascal, когда документация не вполне соответствует действительности.

ПРИМЕЧАНИЕ: аналогично, при использовании строк ресурсов в массиве констант, в массиве будут использоваться только начальные значения строк ресурсов: при переводе отдельных констант элементы в массиве сохранят свое исходное значение.

resourcestring
  Yes = 'Yes.';
  No = 'No.';

var YesNo : array[boolean] of string = (No, Yes);
    B     : boolean; 

begin 
  WriteLn(Yes);       //Выведет Yes
  Writeln(YesNo[B]);  //Выведет No
  B := TRUE;
  Writeln(YesNo[B]);  //Выведет Yes
end.

Вызов Writeln(YesNo[B]) напечатает Yes. или No. в зависимости от значения переменной B, даже если константы Yes и No локализованы каким-то механизмом локализации (переведены на другой язык).

Ну вот как-то так. Однако, такая простая вещь, как константы, в руках профессионала может оказаться очень даже не простой...


Директивы компилятора Директивы компилятора
Как это ни странно, но даже многие опытные программисты не используют директивы компилятора, считая их чем-то ненужным и бесполезным. А между тем, директивы компилятора - это очень классная штука. Если их умело применять в своих программах, то можно существенно сократить время на разработку и уменьшить количество рутинных операций. Подробнее...
Инфо-МАСТЕР ®
Все права защищены ©
e-mail: mail@info-master.su

Яндекс.Метрика