Компонентный Паскаль/Переменные и константы

Типы данных править

Под понятием "Тип данных" скрывается семантическое свойство информации. Для компьютера, все байты информации представляют собой пакеты из 8 бит. Каждый из битов может быть включенным или выключенным. В зависимости от того, как принято понимать эти состояния (прямая или инверсная логика) -- эти значения принимаются равными "0" или "1". Больше о содержимом ячеек памяти компьютер не знает ничего.

Но для программиста даже один байт может содержать различные типы информации. Например, 8 бит (= 1 байт), с точки зрения программиста -- может содержать числа от 0 до 255. А может и код символа в в однобайтовой кодировке ("а", "б", "в"...). А может и вообще что-то иное. Но все байты одинаковы, у них нет признака того, что они хранят[1]. В какой-то момент, программист вполне может решить, что вот эта ячейка памяти хранит число от 0 до 255. А на самом деле, в самом начале, программист задумывал хранить в этой ячейке памяти код символа для последующей печати. Более того, вполне может быть, что программист решил использовать под свои нужды не одну, а сразу две (или даже тысячу) ячеек? Помнить где расположена каждая ячейка? И такие ошибки встречаются и у новичков, и у опытных программистов. Как решить эту проблему?

Проблема решается тем, что помнить о том, где расположена каждая ячейка памяти, и что она хранит -- должен Компонентный Паскаль. Этой информации нет в самой ячейке, зато есть у языка программирования. Это и есть семантическое свойство информации, в данном случае -- её тип. Таким образом описанная ячейка называется переменная. Свой тип она не меняет с момента её описания, до момента окончания выполнения программы. Исключений не бывает. [2]

Целые числа править

Логический тип править

Логический тип, в полном смысле этого слова не является целым. Но и к дробным числам этот тип можно отнести ещё меньше. Переменная такого типа может принимать только два значения. В Компонентном Паскале эти два значения определены как TRUE ("Истина") и FALSE("ложь"). Этот тип переменных используется даже шире, чем об этом задумываются многие программисты. Логический тип можно использовать явно (через переменную), а можно и неявно (например, через сравнение двух чисел, строк, результатов вызовов процедур). Обозначается такой тип через ключевое слово BOOLEAN. Пример:

flagZ : BOOLEAN; (* флаг признака нуля *)
sep0  : BOOLEAN; (* сепаратор; отвечает за разделение глав *)

Стоит обратить внимание на выравнивание двоеточий и ключевых слов. Двоеточие, если проводить аналогию с русским языком может выступать как указатель на обстоятельство, как во фразе: "Итак: всё плохо!". Только в случае с КП этот разделитель служит для указания типа (справа) для переменной (что слева).

Байтовый тип править

Или просто байт. Обозначается ключевым словом BYTE. Переменная такого типа может принимать значения от 0 до 255. Это совсем не много, но для многих целей может оказаться вполне достаточно. Например, не существует минут и секунд более 60. Или например не бывает дня в месяце с номером 32. Пример описания переменной типа BYTE:

day   : BYTE;
level : BYTE;

Важно не забывать ставить точку с запятой после всех определений переменных (после определения последней переменной точку с запятой можно не ставить). Кроме того, следует помнить, что особенности современных компьютеров: они не работают с одним байтом, они работают, скажем, сразу с 4-мя байтами. Поэтому возможны такие эффекты, как неэкономное расходование памяти. Как избежать таких эффектов будет рассмотрено в дальнейших частях.

Короткое целое править

Короткое целое число в БлэкБоксе/КП определено как 2 байта. В последней редакции сказано, что размеры типов не зависят от аппаратной платформы, но в текущей реализации КП, которая была скомпилирована под архитектуру 32 бита, короткое целое именно 2 байта. Диапазон чисел, которые умещаются в эти 2 байта составляет примерно от -32000 до +32000. Короткое целое обозначается ключевым словом SHORTINT.

Пример объявления коротких целых:

count_pak : SHORTINT; (*счётчик пачек *)
all_palet : SHORTINT; (*всего паллет*)

Как видно, в целом, определение переменных базовых типов однообразно и их легко запомнить.

Целое править

Целое число является основным типом целых чисел для машин с 32 битами на машинное слово. Для КП это именно тот случай. Целое число занимает в памяти 4 байта. Такого количества памяти хватает на описание числа примерно от -2,1 млрд. до +2,1 млрд. Не часто встречаются числа с таким динамическим размахом. Целый тип описывается ключевым словом INTEGER:

count1 : INTEGER;
count2 : INTEGER;

И здесь ничего нет такого, чтобы потребовало особого способа описания переменных.

Длинное целое править

Самый широкий диапазон целых чисел, который встроен в КП. Занимает 8 байт, представляет целые числа в диапазоне примерно от -9,2*10^18 до 9,2*10^18. Даже сложно представить, где такие числа вообще могут потребоваться обычным людям. Обозначаются такие переменные как LONGINT:

dist_solar  : LONGINT; (* расстояние до Солнца *)
dist_pluton : LONGINT; (* расстояние до Плутона *)

Следует помнить, что сборка BlackBox Red (впрочем, как и другие) оптимизированы под 32-х битную архитектуру, поэтому работа с такими числами будет существенно медленней, чем с типом INTEGER.

Вещественные числа править

Вещественные (дробные, рациональные) числа называются так потому, что в окружающем мире редко встречаются "целые" объекты. Например, слоны. Они вроде все слоны. Но слонёнок по массе -- это целый слон? Если нет, то как отразить его массу через целого слона? Кроме того, очень часто приемлемо записывать числа с заданной точностью. Они для этого подходят как никто. Таким образом, вещественные числа находят более чем широкое применение в промышленности.

Короткое вещественное править

Такие числа соответствуют вещественным числам в языке Си. В памяти они занимают 4 байта, но в отличии от целых чисел они имеют особый формат при хранении. Это приводит к тому, что точность таких чисел ограничивается 7-8 десятичных цифр. На зато диапазон этих чисел раздвигается до -3,4*10^(38)...-10^(-38) в отрицательной области, и до 10^(-38)...3,4*10^(38) в области положительных чисел. Даже по сравнению с типом LONGINT это очень много. Но есть и обратная сторона медали. Если в типе LONGINT точность до последнего знака, то в данном случае (как уже было выше упомянуто) только до 7-8. Поэтому, если в вычислениях важна точность, надо помнить о том, что точность больших чисел огрубляет точность малых чисел. Это правило определяет порядок работы с вещественными числами: "сначала маленькие, потом большие" при увеличении, и "сначала большие, потом маленькие" при уменьшении. Такие числа обозначаются ключевым словом SHORTREAL:

mas_zil   : SHORTREAL; (* масса автомобиля ЗиЛ *)
mas_kamaz : SHORTREAL; (* масса автомобиля КамАЗ *)

Отдельно стоит упомянуть то, что вещественные числа обрабатываются на математическом сопроцессоре, и обычно, работа с вещественными числами происходит медленней, чем с целыми.

Вещественное число править

Этот тип чисел занимает в памяти в 2 раза ячеек больше, чем короткое вещественное -- 8 байт и соответствует числу с двойной точностью стандарта "IEEE 754". Диапазон, который охватывают такие числа, если записывать от руки без научного формата -- утомит очень быстро (примерно -10^308...10^308). Точность составляет 15-17 десятичных знаков. Если через метры записывать диаметры ядер атомов -- такой точности как раз должно хватить[3]. Переменная вещественного типа описывается ключевым словом REAL:

mas_andro  : REAL; (* масса галактики Андромеды *)
mas_galac  : REAL; (* масса нашей галактики *)


Литерные типы править

Литерными типами называют такие типы данных, которые имеют какое-либо отношение к отображению. Например, буквы, строки, тексты, цифры, управляющие символы (перевод строки, новая строка, гудок и т.д.). Дело в том, что такие символы крайне важны для человека (и к ним совершенно равнодушен компьютер). Но на экране, принтере, плоттере -- любой из этих символов состоит из множества точек (матрицы). И такая матрица может достигать размера 1200х2400 точек. Да ещё и они могут быть цветные, и таким образом потребуется непомерное количество байтов для хранения всех возможных изображений литер и их цветов. И это ещё не говоря о всяких графических пиктограммах (смайлики, флажки, стрелки, дома и т.д.). Поэтому в своё время был предложен компромиссный вариант для хранения литер. Суть идеи состояла в том, что печатной (служебной) литере должен соответствовать свой код-число. А уж если потребуется, потом можно добавить различные способы вывода кода этой литеры на экран, принтер, плоттер и т.д. Тогда хранение литеры в памяти компьютера становится компактным и универсальным.

Литеры набора Latin-1 править

Эти литеры занимают в памяти ПК всего 1 байт. Если речь идёт исключительно о латинском алфавите (22 буквы), то им вполне можно пользоваться. Но вот проблема: если будет желание выводить символы на национальном алфавите, вместо ожидаемого результата будет непонятно что. А суть этой проблемы в том, что этот набор литер принимался как стандарт на заре компьютерной эпохи. Мало кто задумывался об этой проблеме, поэтому литеры набора Latin-1 даны скорее для обратной совместимости со старыми программами, чем для реального использования. Переменные такого типа описываются ключевым словом SHORTREAL:

lit_a : SHORTCHAR; (* латинская литера "а" *)
lit_b : SHORTCHAR; (* латинская литера "б" *)

Стоит добавить, что кроме букв и цифр в литерах Latin-1 есть ещё и разные интересные значки, которые, иногда, могут и пригодиться [4].

Литеры набора Unicode править

Этот набор литер по сравнению с предыдущим является более прогрессивным. Он лишён недостатков Latin-1, но у каждой медали две стороны. Да, теперь в этот набор Unicode помещаются литеры всех языков мира существующих, или когда-либо существовавших. Туда же помещаются различного рода пиктограммы из всех сфер жизни (значки Солнца, Луны, Земли и даже "Серп и молот"). Но, если байтовые литералы было легко сравнивать, так как они располагались в алфавитном порядке, то как понять, какой код меньше и на каком основании: английский литерал "а" или русский литерал "а"? А это совершенно разные литералы. К счастью, все (или почти все) процедуры для работы с литералами Unicode написаны, и сомнительно, что программисту придётся писать что-то своё (с высокой степенью вероятности это будет велосипед, как говорят программисты). Такой тип переменных описывается ключевым словом CHAR:

lit_a_en : CHAR; (* английская литера "a" *)
lit_a_ru : CHAR; (* русская литера "а" *)

Ещё раз стоит обратить внимание -- в данном примере (в наборе Unicode) русские и английские литеры кодируются различными кодами, хотя внешне и выглядят одинаково[5].

Константы править

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

  • Тип констант определяется автоматически. Т.е. программисту не нужно думать при описании константы о том, какой тип данных должен наилучшим образом соответствовать именно этой константе.
  • Преимущество констант перед переменными также в том, что если программист забудется, и попытается работать с константой, как с переменной -- компилятор КП настойчиво напомнит программисту о том, что он сам запретил менять константу. И это приведёт к избежанию ошибок разработки и исполнения.
  • Также константы заметно быстрее обрабатываются компьютером, чем переменные.

Форма их определения существенно отличается от формы определения переменных:

fail   = 1;
eee    = 2;
good   = 3;
wery_good = 4;
wou    = 5;

Форма описания констант определена не через двоеточие, а через знак "равно". И в данном случае это вполне соответствует законам логики и математики. Также стоит обратить внимание, что константа "wery_good" и её знак "равно" не выровнены со всеми константами. Это допущение вполне приемлемо при оформлении кода. Ну что делать, если имя переменной, такое длинное?

Преобразования типов править

Язык Компонентный Паскаль был спроектирован, а не сочинён. Поэтому правила преобразования типов просты, понятны и предсказуемы.

Преобразования числовых типов править

Как уже было выше описано, самым мощным диапазоном представления чисел является тип REAL. В случае преобразований при необходимости, этот тип преобразуется в более ограниченный -- SHORTREAL. Если этот тип придётся преобразовывать, он в свою очередь сужается до LONGINT. Тип длинное целое при сжатии переходит в тип INT (4 байта). После типа целого сжатие диапазона идёт в сторону SHORTINT (2 байта). Короткое целое тоже может быть сжато до BYTE. Дальше диапазон числа уменьшить нельзя. Тип BOOLEAN, строго говоря числовым не является, хотя и содержит логические "0" и "1". Обратное преобразование также верно. Например, если разделить два целых числа 3 и 2 -- результат будет вещественное число. КП прекрасно понимает, что без этого результат очень неточным. Поэтому, если результат такого деления попытаться присвоить целочисленной переменной -- такой модуль даже не удастся скомпилировать -- Компонентный Паскаль просто не позволит это сделать! В то же время, если сумма двух целых превышает динамический диапазон целого -- КП на стадии компиляции постарается выяснить этот факт, и потребует результат присваивать длинному целому. Если выяснить на этапе компиляции это невозможно, КП во время исполнения остановит программу, и не позволит проскочить момент переполнения и "улететь программе в космос". Те же самые правила касаются и остальных преобразований типов. Коротко схему преобразования типов можно отобразить так:

REAL >= SHORTREAL >= LONGINT >= INTEGER >= SHORTINT >= BYTE

Преобразования литеральных типов править

То, что выше было написано про числовые типы, применимо и к литеральным типам. Также надо учитывать, что приведение SHORTCHAR к CHAR будет затруднено, так как КП просто не будет знать, какая была национальная кодировка типа SHORTCHAR. Коротко схему преобразования типов можно отобразить так:

CHAR >= SHORTCHAR


Использование переменных и констант править

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

  • Описание переменных всегда в КП выносится в отдельную секцию модуля, которая обозначается ключевым словом VAR (variable, переменная).
  • Описание констант всегда в КП выносится в отдельную секцию модуля, которая обозначается ключевым словом CONST (constante, постоянная/неизменяемая).

Hello02.odc

 
Текст программы и результат её обработки и выполнения.
MODULE TestHello02;
	(* это вторая программа на языке
	Компонентный Паскаль. Она выполняет
	кое-какие математические операции *)
	
	IMPORT Kernel, Log, Math;
	CONST
		c = 2;

	VAR
		i: INTEGER;
		i1: REAL;

	PROCEDURE Start*;
		VAR
	BEGIN
		i := 3;
		i1 := c / i;
		Log.String('Результат программы: ');
		Log.Real(i1);
		Log.Ln
	END Start;

BEGIN
END TestHello02.

Необходимо обратить внимание, что константе "с" не нужно присваивать значение, а её тип (судя по всему) компилятор определил, как INTEGER. Наоборот, переменной "i" необходимо присвоить значение, так как при запуске программы, в ней находится "мусор" -- случайные значения, оставшиеся от работы предыдущей программы (которая пользовалась этим участком памяти). Переменной "i1" также не нужно присваивать начальное значение, так как нам оно -- не интересно. Переменная "i1" получает своё значение в результате вычислений.

Если всё сделано правильно, то можно будет увидеть результат, примерно такой, как на скриншоте справа. Удивляемся размеру программы (88 байт), убеждаемся, что деление двух целых чисел привело к результату REAL, вспоминаем как вставить КОММАНДЕР и использовать его.

Немного о присваивании править

В коде представленном выше используется знак равно в двух вариантах:

CONST
    с = 2;
.............<scip>...............
    i  := 3;
    i1 := c/i;

Здесь могут возникнуть вопросы, поэтому ниже приводятся необходимые пояснения:

  • В первом случае, при присвоении константе "с" значения "3" -- стоит знак равно. И это правильная математическая форма записи.
  • Во втором случае, переменная "i1" содержит "мусор", который никак не может быть равен "с/i", и чтобы подчеркнуть этот факт, что это не математическое уравнение, а инструкция присваивания в КП принято в инструкциях использовать символ ":=", как не нарушающий математические соглашения.
  • В ряде языков (в том числе, таком популярном, как Си) знак равно используется и для сравнения чисел в условиях, и это очень часто является источником ошибок. В Компонентном Паскале такие ситуации исключены. Ведь этот язык (в том числе) и для промышленного программирования.

Примечания править

  1. Вообще, существуют компьютеры, которые контролируют типы данных на этапе исполнения программы и обработки данных. Например, см. "Эльбрус-3М1, ОС Эльбрус"
  2. Из-за того, что тип переменных не меняется -- Компонентный Паскаль и является языком со статической типизацией. А такой язык, как python позволяет менять типы переменных, поэтому он относится к языкам программирования с динамической типизацией.
  3. По форматам вещественных чисел стоит посмотреть материал отдельно: Число_одинарной_точности, Число_двойной_точности
  4. Также набор символов Latin-1 известен как ISO_8859-1, первая часть этой таблицы -- ASCII
  5. Кодировка Unicode (Юникод) весьма сложна, убедиться в этом можно прочитав статью про Юникод

.