Инструменты пользователя

Инструменты сайта


blackbox:manual:var-const

1.6 Переменные и константы

1. Типы данных

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

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

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

2. Целые числа

2.1 Логический тип

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

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

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

2.2 Байтовый тип

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

день   : BYTE;
уровень: BYTE;

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

2.3 Короткое целое

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

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

кцПачк_всего : SHORTINT; (*счётчик пачек *)
кцПалет_всего: SHORTINT; (*всего палет*)

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

2.4 Целое

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

цСчётчик1: INTEGER;
цСчётчик2: INTEGER;

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

2.5 Длинное целое

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

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

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

3. Вещественные числа

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

3.1 Короткое вещественное

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

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

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

3.2 Вещественное число

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

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

4. Литерные типы

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

4.1 Литеры набора Latin-1

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

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

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

4.2 Литеры набора Unicode

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

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

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

5. Константы

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

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

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

_отказ = 1;
_добро = 2;
_хор   = 3;
_оч_хор = 4;
_вау   = 5;

Форма описания констант определена не через двоеточие, а через знак «равно». И в данном случае это вполне соответствует законам логики и математики. Также стоит обратить внимание, что константа _оч_хор и её знак «равно» не выровнены со всеми константами. Это допущение вполне приемлемо при оформлении кода. Ну что делать, если имя переменной, такое длинное? В данном примере все константы начинаются с символа подчёркивания. Такого требования нет в стиле по кодированию (code style), но авторы рекомендуют именно такой подход, чтобы было легче различать константы.

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

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

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

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

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

Если нужно пройти в обратном направлении, то КП сначала попытается привести результат к более мощному типу, и если динамического диапазона обоих типов не хватает для хранения результата — КП потребует присвоения результата переменной, с заведомо более мощным типом. Отдельно следует отметить, что в КПнеявное приведение типов запрещено (такое приведение может привести к отрицательным побочным последствиям, типичный пример – Си).

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

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

CHAR => SHORTCHAR

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

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

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

Hello02.odc

MODULE КнигаПривет2;
	(* это вторая программа на языке
	Компонентный Паскаль. Она выполняет
	кое-какие математические операции *)
 
	IMPORT Log;
	CONST
		_конст = 2;
 
	VAR
		цПерем1: INTEGER;
		цПерем2: REAL;
 
	PROCEDURE Старт*;
		VAR
	BEGIN
		цПерем1 := 3;
		цПерем2 := _конст / цПерем1;
		Log.String('Результат программы: ');
		Log.Real(цПерем2);
		Log.Ln
	END Старт;
 
END КнигаПривет2.

Вывод программы:

компилируется "КнигаПривет2"   88   12
Результат программы:  0.6666666666666666
     

Для того, чтобы скомпилировать программу и выгрузить необходимо традиционно нажать <Ctrl+K> и запустить программу на исполнение через КОММАНДЕР (вставляется <Ctrl+Q>):

(^)TestHello02.Start

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

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

Кроме того, надо обратить внимание как на этот раз был выполнен импорт модуля Log - вместо его родного имени, теперь используется мЛог. По русски, с префиксом «м»(«модуль»), что делает код чуть более понятным. Этим полезным способом будем пользоваться и далее.

8. Немного о присваивании

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

CONST
    _конст = 2;
.............<skip>...............
    цПерем  := 3;
    вПерем := c/i;

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

  1. В первом случае, при присвоении константе _конст значения 3 — стоит знак равно. И это правильная математическая форма записи.
  2. Во втором случае, переменная вПерем содержит мусор, который никак не может быть равен _конст/цПерем (нет, конечно может, но вероятность такого совпадения асимптотически стремится к нулю), и чтобы подчеркнуть этот факт, что это не математическое уравнение, а инструкция присваивания в КП принято в инструкциях использовать символ :=, как не нарушающий математические соглашения.
  3. Как видно из последней строки, выполнено неявное преобразование типов. Тип INTEGER приведён к типу REAL. Компонентный Паскаль соблюдает разумную необходимость с достаточностью в таких вопросах. Существует приличное число языков, где вообще не требуется выполнять явное приведение типов, или наоборот: на каждый чих необходимо вмешательство программиста. Едва ли оба подхода, как крайности — рациональны.

В ряде языков (в том числе, таком популярном, как Си) знак «равно» используется и для сравнения чисел в условиях, и это очень часто является источником ошибок. В Компонентном Паскале такие ситуации исключены. Ведь этот язык (в том числе) и для промышленного программирования.

Также необходимо обратить внимание, что может возникнуть соблазн, с целью ускорения исполнения программы приводить числа к более компактному виду, например REAL к SHORTREAL, полагая, что менее мощный тип обрабатывается быстрее. На самом деле, компилятор КП, как того требует программист, приведёт число к более компактному типу, для вычислений опять переведёт в более мощный тип, и чтобы согласовать результат вычислений с конечной переменной — ещё раз преобразует к более компактному виду. Таким образом, не только не будет прироста скорости вычислений, но и заметная её потеря. Основными типами чисел в КП являются INTEGER и REAL. Именно ими и стоит пользоваться. Все остальные базовые типы чисел нужны только для межмашинного обмена данными, либо экономии оперативной памяти.

9. Заключение

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

1)
Вообще, существуют компьютеры, которые контролируют типы данных на этапе исполнения программы и обработки данных. Например, см. Эльбрус-3М1, ОС Эльбрус
2)
Из-за того, что тип переменных не меняется — Компонентный Паскаль и является языком со статической типизацией. А такой язык, как python позволяет менять типы переменных, поэтому он относится к языкам программирования с динамической типизацией.
3)
По форматам вещественных чисел стоит посмотреть в Википедии материал отдельно: «Число одинарной точности», «Число двойной точности»
4)
Также набор символов Latin-1 известен как ISO_8859-1, первая часть этой таблицы — ASCII
5)
Кодировка Unicode (Юнико́д или Унико́д) весьма сложна, убедиться в этом можно прочитав статью в Википедии — «Юникод»
6)
Но стоит помнить, что какой бы компилятор умный не был – он просто не знает, какими числами ему придётся оперировать во время исполнения
blackbox/manual/var-const.txt · Последнее изменение: 2020/10/29 07:08 (внешнее изменение)