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

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


bb:redbook:106

Различия

Показаны различия между двумя версиями страницы.

Ссылка на это сравнение

Предыдущая версия справа и слева Предыдущая версия
Следующая версия
Предыдущая версия
bb:redbook:106 [2016/04/07 22:47]
prospero78 [10. Примечания]
— (текущий)
Строка 1: Строка 1:
-===== 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''. Пример: 
- 
- > flagZ : BOOLEAN; (* флаг признака нуля *) 
- > sep0  : BOOLEAN; (* сепаратор; отвечает за разделение глав *) 
-  
-Стоит обратить внимание на выравнивание двоеточий и ключевых слов. Двоеточие, если проводить аналогию с русским языком может выступать как указатель на обстоятельство, как во фразе: "Итак: всё плохо!". Только в случае с КП этот разделитель служит для //указания типа// (справа) для переменной (что слева). 
- 
-=== 2.2 Байтовый тип === 
-Или просто байт. Обозначается ключевым словом ''BYTE''. Переменная такого типа может принимать значения от 0 до 255. Это совсем не много, но для многих целей может оказаться вполне достаточно. Например, //не существует// минут и секунд более 60. Или например //не бывает// дня в месяце с номером 32. Пример описания переменной типа ''BYTE'': 
- 
- > day   : BYTE; 
- > level : BYTE; 
-  
-Важно не забывать ставить //точку с запятой// после всех определений переменных (после определения последней переменной точку с запятой тоже нужно ставить). Кроме того, следует помнить, что особенности современных компьютеров: они не работают с одним байтом, они работают, скажем, сразу с 4-мя байтами. Поэтому возможны такие эффекты, как неэкономное расходование памяти. Как избежать таких эффектов будет рассмотрено в дальнейших частях. 
- 
-=== 2.3 Короткое целое === 
-Короткое целое число в **БлэкБоксе/КП** определено как 2 байта. В редакции 1.6 сказано, что размеры типов //не зависят// от аппаратной платформы, но в текущей реализации **КП**, которая была скомпилирована под архитектуру 32 бита, короткое целое именно 2 байта. Диапазон чисел, которые умещаются в эти 2 байта составляет примерно от -32000 до +32000. Короткое целое обозначается ключевым словом ''SHORTINT''. 
- 
-Пример объявления коротких целых: 
- 
- > count_pak : SHORTINT; (*счётчик пачек *) 
- > all_palet : SHORTINT; (*всего палет*) 
- 
-Как видно, в целом, определение переменных базовых типов однообразно и их легко запомнить. 
-  
-=== 2.4 Целое === 
-//Целое число// является //основным// типом целых чисел для машин с 32 битами на машинное слово. Для **КП** это именно тот случай. Целое число занимает в памяти 4 байта. Такого количества памяти хватает на описание числа примерно от -2,1 млрд. до +2,1 млрд. Не часто встречаются числа с таким динамическим размахом. Целый тип описывается ключевым словом ''INTEGER'': 
- 
- > count1 : INTEGER; 
- > count2 : INTEGER; 
- 
-И здесь ничего нет такого, чтобы потребовало особого способа описания переменных. 
- 
-=== 2.5 Длинное целое === 
-Самый широкий диапазон целых чисел, который встроен в **КП**. Занимает 8 байт, представляет целые числа в диапазоне примерно от -9,2*10^18 до 9,2*10^18. Даже сложно представить, где такие числа вообще могут потребоваться обычным людям. Обозначаются такие переменные как ''LONGINT'': 
- 
- > dist_solar  : LONGINT; (* расстояние до Солнца *) 
- > dist_pluton : LONGINT; (* расстояние до Плутона *) 
- 
-Следует помнить, что сборка **BlackBox Red** (впрочем, как и другие) оптимизированы под 32-х битную архитектуру, поэтому работа с такими числами будет //существенно медленней//, чем с типом ''INTEGER''. 
-  
-==== 3. Вещественные числа ==== 
-//Вещественные// (дробные, рациональные) числа называются так потому, что в окружающем мире редко встречаются "целые" объекты. Например, слоны. Они вроде все слоны. Но слонёнок по массе — это целый слон? Если нет, то как отразить его массу через целого слона? Кроме того, очень часто приемлемо записывать числа с //заданной точностью//. Они для этого подходят как никто. Таким образом, вещественные числа находят более чем широкое применение в промышленности. 
- 
-=== 3.1 Короткое вещественное === 
-Такие числа соответствуют вещественным числам в языке **Си**. В памяти они занимают 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; (* масса автомобиля КамАЗ *) 
- 
-Отдельно стоит упомянуть то, что вещественные числа обрабатываются на математическом сопроцессоре, и обычно, работа с вещественными числами происходит медленней, чем с целыми. 
- 
-=== 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'': 
- 
- > mas_andro  : REAL; (* масса галактики Андромеды *) 
- > mas_galac  : 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. Заключение ==== 
-Эта глава довольна важна для понимания того, что //базовые типы// (или, как иногда говорят //фундаментальные//) не зря различаются на четыре группы. В природе их представления и обработки есть //коренные отличия//. Конечно, можно было обойтись одним-двумя базовыми типами, но на практике такое ограничение бывает неудобно. Раздувать число базовых типов тоже смысла не имеет — это лишь усложнит овладение языком и снизит надёжность программ. 
  
bb/redbook/106.1460058467.txt.gz · Последнее изменение: 2020/10/29 07:08 (внешнее изменение)