Предыдущая версия справа и слева
Предыдущая версия
Следующая версия
|
Предыдущая версия
|
bb:redbook:106 [2017/08/30 13:47] prospero78 [2.3 Короткое целое] |
— (текущий) |
===== 1.6 Переменные и константы ===== | |
| |
==== 1. Типы данных ==== | |
Под понятием //Тип данных// скрывается //семантическое свойство// информации. Для компьютера все байты информации представляют из себя //пакеты// из 8 бит. Каждый из битов может быть //включенным// или //выключенным//. В зависимости от того, как принято понимать эти состояния (//прямая// или //инверсная логика//) — эти значения принимаются равными "0" или "1". Больше о содержимом ячеек памяти компьютер не знает //ничего//. | |
| |
Но для программиста даже один байт может содержать различные типы информации. Например, 8 бит (= 1 байт), с точки зрения программиста — может содержать //числа// от 0 до 255. А может и //код символа// в однобайтовой кодировке ("а", "б", "в"...). А может и вообще //что-то иное//. Но все байты одинаковы, у них нет признака того, что они хранят. В какой-то момент, программист вполне может решить, что вот эта ячейка памяти хранит число от 0 до 255((Вообще, существуют компьютеры, которые контролируют типы данных на этапе исполнения программы и обработки данных. Например, см. [[https://ru.wikipedia.org/wiki/%D0%AD%D0%BB%D1%8C%D0%B1%D1%80%D1%83%D1%81-3%D0%9C1|Эльбрус-3М1]], [[https://ru.wikipedia.org/wiki/%D0%9E%D0%A1_%D0%AD%D0%BB%D1%8C%D0%B1%D1%80%D1%83%D1%81|ОС Эльбрус]])). А на самом деле, в самом начале, программист задумывал хранить в этой ячейке памяти код символа для последующей печати. Более того, вполне может быть, что программист решил использовать под свои нужды не одну, а //сразу две// (или даже //тысячу//) ячеек? Помнить где расположена //каждая// ячейка? Такое программирование превратится в ад! И такие ошибки встречаются и у новичков, и у опытных программистов. Как решить эту проблему? | |
| |
Проблема решается тем, что помнить о том, где расположена каждая ячейка памяти, и что она хранит — должен **Компонентный Паскаль**. Этой информации нет в самой ячейке, зато есть у языка программирования. Это и есть //семантическое свойство информации//, в данном случае — её //тип//. Таким образом описанная ячейка называется "переменная". Свой тип она //не меняет// с момента её описания, до момента окончания выполнения программы. Исключений не бывает((Из-за того, что тип переменных не меняется — Компонентный Паскаль и является языком со статической типизацией. А такой язык, как [[https://ru.wikipedia.org/wiki/Python|python]] позволяет менять типы переменных, поэтому он относится к языкам программирования с динамической типизацией.)). | |
| |
==== 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×10<sup>18</sup> до 9,2×10<sup>18</sup>. Даже сложно представить, где такие числа вообще могут потребоваться обычным людям. Обозначаются такие переменные как ''LONGINT'': | |
| |
дцСолнце_дист: LONGINT; (* расстояние до Солнца *) | |
дцПлутон_дист: LONGINT; (* расстояние до Плутона *) | |
| |
Следует помнить, что сборка **BlackBox Intron** (впрочем, как и другие) оптимизированы под 32-х битную архитектуру, поэтому работа с такими числами будет //существенно медленней//, чем с типом ''INTEGER''. | |
| |
==== 3. Вещественные числа ==== | |
//Вещественные// (дробные, рациональные) числа называются так потому, что в окружающем мире редко встречаются "целые" объекты. Например, слоны. Они вроде все слоны. Но слонёнок по массе — это целый слон? Если нет, то как отразить его массу через целого слона? Кроме того, очень часто приемлемо записывать числа с //заданной точностью//. Они для этого подходят как никто. Таким образом, вещественные числа находят более чем широкое применение в промышленности. | |
| |
==== 3.1 Короткое вещественное ==== | |
Такие числа соответствуют вещественным числам в языке **Си**. В памяти они занимают 4 байта, но в отличии от целых чисел они имеют //особый формат// при хранении. Это приводит к тому, что точность таких чисел ограничивается в 7-8 десятичных цифр. На зато диапазон этих чисел раздвигается до –3,4×10<sup>38</sup>...–10<sup>–38</sup> в отрицательной области, и до 10<sup>–38</sup>...3,4*10<sup>38</sup> в области положительных чисел. Даже по сравнению с типом ''LONGINT'' это оооочень много. Но есть и обратная сторона медали. Если в типе ''LONGINT'' точность до последнего знака, то в данном случае (как уже было выше упомянуто) //только до 7-8//. Поэтому, если в вычислениях важна //точность//, надо помнить о том, что точность больших чисел //огрубляет// точность малых чисел. Это правило определяет порядок работы с вещественными числами: "сначала маленькие, потом большие" при увеличении, и "сначала большие, потом маленькие" при уменьшении. Такие числа обозначаются ключевым словом ''SHORTREAL'': | |
| |
вЗил_масса : SHORTREAL; (* масса автомобиля ЗиЛ *) | |
вКамаз_масса: SHORTREAL; (* масса автомобиля КамАЗ *) | |
| |
Отдельно стоит упомянуть то, что вещественные числа обрабатываются на математическом сопроцессоре, и обычно, работа с вещественными числами происходит медленней, чем с целыми. | |
| |
==== 3.2 Вещественное число ==== | |
Этот тип чисел занимает в памяти //в 2 раза ячеек больше//, чем короткое вещественное — все 8 байт и соответствует числу с //двойной точностью// стандарта [[https://ru.wikipedia.org/wiki/IEEE_754-2008|"IEEE 754"]]. Диапазон, который охватывают такие числа, если записывать от руки без научного формата — утомит очень быстро (примерно -10^308...10^308). Точность составляет 15-17 десятичных знаков. Если через штуки записывать количество атомов во Вселенной — такой точности как раз должно хватить((По форматам вещественных чисел стоит посмотреть в Википедии материал отдельно: [[https://ru.wikipedia.org/wiki/%D0%A7%D0%B8%D1%81%D0%BB%D0%BE_%D0%BE%D0%B4%D0%B8%D0%BD%D0%B0%D1%80%D0%BD%D0%BE%D0%B9_%D1%82%D0%BE%D1%87%D0%BD%D0%BE%D1%81%D1%82%D0%B8|«Число одинарной точности»]], [[https://ru.wikipedia.org/wiki/%D0%A7%D0%B8%D1%81%D0%BB%D0%BE_%D0%B4%D0%B2%D0%BE%D0%B9%D0%BD%D0%BE%D0%B9_%D1%82%D0%BE%D1%87%D0%BD%D0%BE%D1%81%D1%82%D0%B8|«Число двойной точности»]])). Переменная вещественного типа описывается ключевым словом ''REAL'': | |
| |
вдАндром_масса: REAL; (* масса галактики Андромеды *) | |
вдГалак_масса : REAL; (* масса нашей галактики *) | |
| |
==== 4. Литерные типы ==== | |
//Литерными типами// называют такие типы данных, которые имеют какое-либо отношение к отображению чего-либо. Например, буквы, строки, тексты, цифры, управляющие символы (перевод строки, новая строка, гудок и т.д.). Дело в том, что такие символы //крайне важны// для человека (и к ним совершенно равнодушен компьютер). Но на экране, принтере, плоттере — любой из этих символов состоит из множества точек (матрицы). И такая матрица может достигать размера 2400х4800 точек. Да ещё и они могут быть цветные (разных цветов все точки), и таким образом потребуется непомерное количество байтов для хранения всех возможных изображений литер и их цветов. И это ещё не говоря о всяких графических пиктограммах (смайлики, флажки, стрелки, дома и т. д.). Поэтому в своё время был предложен компромиссный вариант для хранения литер. Суть идеи состояла в том, что печатной (служебной) литере должен соответствовать свой код-число. А уж если потребуется, потом можно добавить различные способы вывода кода этой литеры на экран, принтер, плоттер и т. д. Тогда хранение литеры в памяти компьютера становится компактным и универсальным. | |
| |
=== 4.1 Литеры набора Latin-1 === | |
Эти //литеры// занимают в памяти ПК всего 1 байт. Если речь идёт исключительно о //латинском алфавите// (22 буквы), то им вполне можно пользоваться. Но вот проблема: если будет желание выводить символы на //национальном// алфавите, вместо ожидаемого результата будет //непонятно что//. А суть этой проблемы в том, что этот набор литер принимался как стандарт на заре компьютерной эпохи. Мало кто задумывался об этой проблеме, поэтому литеры набора **Latin-1** даны скорее для //обратной совместимости со старыми программами//, чем для реального использования. Переменные такого типа описываются ключевым словом ''SHORTCHAR'': | |
| |
lit_a : SHORTCHAR; (* латинская литера "а" *) | |
lit_b : SHORTCHAR; (* латинская литера "б" *) | |
| |
Стоит добавить, что кроме букв и цифр в литерах **Latin-1** есть ещё и разные интересные значки, которые, иногда, могут и пригодиться ((Также набор символов **Latin-1** известен как [[https://ru.wikipedia.org/wiki/ISO_8859-1|ISO_8859-1]], первая часть этой таблицы — [[https://ru.wikipedia.org/wiki/ASCII|ASCII]])). | |
| |
=== 4.2 Литеры набора Unicode === | |
Этот набор литер по сравнению с предыдущим является более прогрессивным. Он лишён недостатков **Latin-1**, но у каждой медали две стороны. Да, теперь в этот набор **Unicode** помещаются литеры //всех языков мира// существующих, или когда-либо существовавших. Туда же помещаются различного рода //пиктограммы// из всех сфер жизни (значки Солнца, Луны, Земли и даже "Серп и молот"). Но, если байтовые литералы было легко сравнивать, так как они располагались в алфавитном порядке, то как понять, какой код меньше и на каком основании: английский литерал "а" или русский литерал "а"? А это //совершенно разные// литералы. К счастью, все (или почти все) процедуры для работы с литералами **Unicode** написаны, и сомнительно, что программисту придётся писать что-то своё (с высокой степенью вероятности это будет //велосипед//, как говорят программисты). Такой тип переменных описывается ключевым словом ''CHAR'': | |
| |
lit_a_en : CHAR; (* английская литера "a" *) | |
lit_a_ru : CHAR; (* русская литера "а" *) | |
| |
Ещё раз стоит обратить внимание — в данном примере (в наборе **Unicode**) русские и английские литеры кодируются //различными кодами//, хотя внешне и выглядят //одинаково//((Кодировка **Unicode** (Юнико́д или Унико́д) весьма сложна, убедиться в этом можно прочитав статью в Википедии — [[https://ru.wikipedia.org/wiki/%D0%AE%D0%BD%D0%B8%D0%BA%D0%BE%D0%B4|«Юникод»]])). | |
| |
| |
==== 5. Константы ==== | |
//Константой// называется такая переменная, которую //нельзя// изменять в ходе выполнения программы. Преимущества констант перед переменными можно выразить следующими положениями: | |
| |
- Тип констант определяется //автоматически//. Т. е. программисту //не нужно думать// при описании константы о том, какой тип данных должен наилучшим образом соответствовать именно этой константе. | |
- Преимущество констант перед переменными также в том, что если программист забудется, и попытается работать с константой, как с переменной — компилятор **КП** настойчиво напомнит программисту о том, что он сам //запретил// менять константу. И это приведёт к избежанию ошибок разработки и исполнения. | |
- Также константы заметно //быстрее// обрабатываются компьютером, чем переменные. | |
| |
Форма их определения существенно отличается от формы определения переменных: | |
| |
_fail = 1; | |
_eee = 2; | |
_good = 3; | |
_wery_good = 4; | |
_wou = 5; | |
| |
Форма описания //констант// определена не через двоеточие, а через знак "равно". И в данном случае это вполне соответствует законам логики и математики. Также стоит обратить внимание, что константа ''_very_good'' и её знак "равно" не выровнены со всеми константами. Это допущение вполне приемлемо при оформлении кода. Ну что делать, если имя переменной, такое длинное? В данном примере все константы начинаются с символа подчёркивания. Такого требования //нет// в стиле по кодированию (code style), но авторы рекомендуют именно такой подход, чтобы было легче различать константы. | |
| |
==== 6. Преобразования типов ==== | |
Язык **Компонентный Паскаль** был //спроектирован//, а не //сочинён//. Поэтому правила преобразования типов просты, понятны и предсказуемы. | |
| |
=== 6.1 Преобразования числовых типов === | |
Как уже было выше описано, самым мощным диапазоном представления чисел является тип ''REAL''. В случае преобразований при необходимости, этот тип преобразуется в более ограниченный — ''SHORTREAL''(8 байт с плавающей запятой в 4 байта с плавающей запятой). Если этот тип придётся преобразовывать, он в свою очередь сужается до ''LONGINT'' (4 байта с плавающей запятой в 4 байта целочисленного значения). Тип //длинное целое// при сжатии переходит в тип ''INTEGER'' (8 байт в 4 байта). После типа //целого// сжатие диапазона идёт в сторону ''SHORTINT'' (2 байта). Короткое целое тоже может быть сжато до ''BYTE''. Дальше диапазон числа уменьшить нельзя. Тип ''BOOLEAN'', строго говоря числовым не является, хотя и содержит логические "0" и "1". Обратное преобразование также верно. Например, если разделить два целых числа 3 и 2 — результат будет //вещественное// число. **КП** прекрасно понимает, что без этого результат будет очень неточным. Поэтому, если результат такого деления попытаться присвоить целочисленной переменной — такой модуль даже //не удастся// скомпилировать — **Компонентный Паскаль** просто //не позволит// это сделать! В то же время, если сумма двух целых превышает динамический диапазон целого — **КП** на стадии компиляции //постарается// выяснить этот факт, и, по возможности, потребует результат присваивать длинному целому((Но стоит помнить, что какой бы компилятор умный не был --- он просто не знает, какими числами ему придётся оперировать во время исполнения)). Если выяснить на этапе компиляции это невозможно, **КП** во время исполнения //остановит// программу, и //не позволит// проскочить момент переполнения и "улететь программе в космос". Те же самые правила касаются и остальных преобразований типов. Коротко схему преобразования типов можно отобразить так: | |
| |
REAL => SHORTREAL => LONGINT => INTEGER => SHORTINT => BYTE | |
| |
Если нужно пройти в обратном направлении, то **КП** сначала попытается привести результат к более //мощному// типу, и если динамического диапазона обоих типов не хватает для хранения результата — **КП** потребует присвоения результата переменной, с заведомо более мощным типом. | |
| |
=== 6.2 Преобразования литеральных типов === | |
| |
То, что выше было написано про числовые типы, применимо и к литерным типам. Также надо учитывать, что приведение ''SHORTCHAR'' к ''CHAR'' будет затруднено, так как **КП** просто //не будет знать//, какая была //национальная кодировка// типа ''SHORTCHAR''. Коротко схему преобразования типов можно отобразить так: | |
| |
CHAR => SHORTCHAR | |
==== 7. Использование переменных и констант ==== | |
В этом разделе будет приведён пример, показывающий как использовать переменные различных типов. Прежде чем будет приведён полный текст программы, кое-какие пояснения: | |
| |
- Описание переменных всегда в **КП** выносится в отдельную секцию модуля, которая обозначается ключевым словом ''VAR'' (variable, переменная). | |
- Описание констант всегда в **КП** выносится в отдельную секцию модуля, которая обозначается ключевым словом ''CONST'' (constante, постоянная/неизменяемая). | |
| |
Hello02.odc | |
<code oberon2> | |
MODULE TestHello02; | |
(* это вторая программа на языке | |
Компонентный Паскаль. Она выполняет | |
кое-какие математические операции *) | |
| |
IMPORT Log; | |
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. | |
</code> | |
Вывод программы: | |
компилируется "TestHello02" 88 12 | |
Результат программы: | |
0.6666666666666666 | |
| |
Для того, чтобы скомпилировать программу и выгрузить необходимо традиционно нажать **<Ctrl+K>** и запустить программу на исполнение через **КОММАНДЕР** (вставляется **<Ctrl+Q>**): | |
| |
(^)TestHello02.Start | |
| |
Необходимо обратить внимание, что константе ''с'' не нужно присваивать значение, а её //тип// (судя по всему) компилятор определил, как ''INTEGER''. Наоборот, переменной ''i'' необходимо присвоить значение, так как при запуске программы, в ней может находиться //мусор// (если это //локальная// переменная, но в нашем случае — //глобальная// — а значит, содержится 0). //Мусор// — это //случайные// значения, оставшиеся от работы предыдущей программы (которая пользовалась этим участком памяти). Переменной ''i1'' также не нужно присваивать начальное значение, так как нам оно — не интересно. Переменная ''i1'' получает своё значение в результате вычислений. | |
| |
Если всё сделано правильно, то можно будет увидеть результат, примерно такой, как на врезке выше. Удивляемся размеру программы (88 байт), убеждаемся, что деление **двух целых чисел** привело к результату ''REAL'', вспоминаем как вставить **КОММАНДЕР** и использовать его. | |
==== 8. Немного о присваивании ==== | |
В коде представленном выше используется знак равно в двух вариантах: | |
| |
CONST | |
с = 2; | |
.............<skip>............... | |
i := 3; | |
i1 := c/i; | |
| |
Здесь могут возникнуть вопросы, поэтому ниже приводятся необходимые пояснения: | |
| |
- В первом случае, при присвоении константе ''с'' значения ''3'' — стоит знак равно. И это правильная математическая форма записи. | |
- Во втором случае, переменная ''i1'' содержит //мусор//, который никак не может быть равен ''с/i'' (нет, конечно может, но вероятность такого совпадения асимптотически стремится к нулю), и чтобы подчеркнуть этот факт, что это //не математическое уравнение//, а инструкция //присваивания// в **КП** принято в инструкциях использовать символ '':='', как не нарушающий математические соглашения. | |
- Как видно из последней строки, выполнено неявное преобразование типов. Тип ''INTEGER'' приведён к типу ''REAL''. **Компонентный Паскаль** соблюдает разумную необходимость с достаточностью в таких вопросах. Существует приличное число языков, где вообще //не требуется// выполнять //явное приведение типов//, или наоборот: на каждый чих необходимо вмешательство программиста. Едва ли оба подхода, как крайности — рациональны. | |
| |
В ряде языков (в том числе, таком популярном, как **Си**) знак "равно" используется и для сравнения чисел //в условиях//, и это очень часто является источником ошибок. В **Компонентном Паскале** такие ситуации исключены. Ведь этот язык (в том числе) и для //промышленного программирования//. | |
| |
Также необходимо обратить внимание, что может возникнуть соблазн, с целью ускорения исполнения программы приводить числа к более компактному виду, например ''REAL'' к ''SHORTREAL'', полагая, что //менее мощный// тип обрабатывается //быстрее//. На самом деле, компилятор **КП**, как того требует программист, приведёт число к более компактному типу, для вычислений //опять// переведёт в более мощный тип, и чтобы согласовать результат вычислений с конечной переменной — ещё раз преобразует к более компактному виду. Таким образом, не только //не будет// прироста скорости вычислений, но и заметная её //потеря//. Основными типами чисел в **КП** являются ''INTEGER'' и ''REAL''. Именно ими и стоит пользоваться. Все остальные базовые типы чисел нужны только для //межмашинного обмена данными//, либо экономии оперативной памяти. | |
| |
==== 9. Заключение ==== | |
Эта глава довольна важна для понимания того, что //базовые типы// (или, как иногда говорят //фундаментальные//) не зря различаются на четыре группы. В природе их представления и обработки есть //коренные отличия//. Конечно, можно было обойтись одним-двумя базовыми типами, но на практике такое ограничение бывает неудобно. Раздувать число базовых типов тоже смысла не имеет — это лишь усложнит овладение языком и снизит надёжность программ. | |
| |