Вы находитесь на странице: 1из 48

УДК 681.142.

2
Вопросы разработки языков программирования высокого
уровня. Учебное пособие по дисциплине "Языки
программирования высокого уровня". Часть 1. Москва,
Московский институт радиотехники, электроники и автоматики,
2004, стр.50.

Изложены некоторые вопросы построения языков


программирования высокого уровня. Приведены различные
оценочные критерии и классификации языков. Рассмотрены такие
понятия как связывание объекта с атрибутами, время жизни
переменной, контроль типов, уровни типизации языка,
эквивалентность типов, области видимости. Описана система
типов языка программирования. Дается сравнительный анализ
основных императивных языков.

Автор: Лукинова О.В., к.т.н.

Рецензенты: Гребенюк Е.И., к.т.н.


Болквадце Г.Р., к.т.н.

Утверждено на заседании
кафедры № 252 1 декабря 2004 г.

© МИРЭА, базовая кафедра №252

Тырл ж 50 жз
Глава 1. Оценочные характеристики
языков программирования

1.1. Введение
История развития языков программирования (ЯП) не
отделима от развития вычислительной техники, т.к. любой ЯП
отражает внутреннюю структуру электронно-вычислительной
машины. В основе же любой современной архитектуры ЭВМ
лежит так называемая машина Неймана, архитектурно-функцио-
нальные принципы построения которой были сформулированы
венгерским математиком Фон Нейманом в 1946 г.
Принципы Фон Неймана:
1. Программное управление работой ЭВМ. Программа,
реализующая алгоритм задачи, состоит из команд, каждая
команда осуществляет единичный акт преобразования
информации. Все разновидности команд данной ЭВМ
составляют систему команд этой ЭВМ.
2. Наличие команд условного перехода, т.е. переход на любой
участок программы в зависимости от условий. Этот принцип
позволяет ввести также циклические конструкции,
осуществляющие итерационные вычисления.
3. Принцип хранимой программы, т.е. в машине всегда должно
быть Запоминающее Устройство (ЗУ), которое хранит команды
и данные, при этом данные отправляются в Арифметико-
логическое устройство (АЛУ), а команды - в Устройство
Управления (УУ). Способ выборки команд и данных из памяти
и время выборки одинаковы.
4". Иерархичность ЗУ. Несоответствие между быстродействием
АЛУ и ЗУ частично преодолевается введением в архитектуру
различной по быстродействию памяти:
- быстрая регистровая память для хранения управляющей
информации в процессе счета;
- ОП (оперативная память), в которой хранятся данные и сама
программа во время счета;
- долговременная память (винчестеры, дискеты, CD).
5. Двоичная система счисления для кодирования информации,
которая обрабатывается ЭВМ. Это позволяет иметь:
- достаточно простую элементную базу;
- минимальную единицу измерения информации в1 бит (или 0);
- реализовать операция умножения с помощью сложения и
последующего сдвига разрядов.

1.2. Категории языков программирования.


1. Императивные языки (Fortran, Algol, С, С++, Pascal и пр.)
Именно эти языки отражают принципы Фон Неймана
посредством:
а) понятия "переменной", являющейся аналогом ячейки
оперативной памяти;
б) операторов ввода/вывода информации в ячейки;
в) операторов присваивания, отражающих пересылки
информации между ячейками;
г) команд управления (условного перехода, циклических
конструкций).
2. Функциональные языки (LISP) осуществляют вычисления с
помощью функций. Не содержат переменных, операторов
присваивания и итераций. Требует для реализации не
Неймановскую архитектуру, однако таковой на сегодняшний
день не существует.
3. Логические языки (Prolog) основаны на продукционных
правилах. Здесь не существует порядка выполнения команд.
Система реализации ЭВМ сама выбирает порядок выполнения
команд, который приведет нужному результату.
4. ЯП объектно-ориентированного программирования (ООП). Все
наиболее используемые на сегодняшний день ЯП, такие как
Visual С++, Delphi, Fortran90, ADA 95, содержат объектную
%

модель данных и обладают наряду с императивными, также


свойствами ООП. Однако только Smalltalk80 остается
единственным в чистом виде языком ООП

1.3. Классификация применений


языков программирования.
1. Научные приложения (Fortran), для которых обязательным
требованием были наличие:
- арифметики с плавающей точкой, т.е. реальных чисел;
- массивов (операций с векторами и матрицами); *
-циклических конструкций для осуществления итерационных
вычислений.
2. Коммерческое направление (Cobol, электронные таблицы). Здесь
необходимо обеспечить:
- выполнение операций с фиксированной точкой;
- генерацию различных отчетов, а, следовательно, должна быть
мощная обработка строковых данных;
- возможность обрабатывать большие объемы данных.
3. Искусственный интеллект (Lisp, Prolog). Задачи, решаемые с
помощью ЯП искусственного интеллекта - обработка текстовых
символов.
4. В системном программировании (С) необходимы средства
низкого уровня для написания программ связи с аппаратурой
(драйверов).
5. Специализированные языки решают задачи управления
различными устройствами.

1.4. Характеристики основных языков.


На развитие языков программирования высокого уровня
оказывают влияние несколько факторов. Во-первых, архитектура
вычислительной машины, во-вторых, методологии
программирования. В этой связи следует упомянуть о трех
направлениях:
1. структурное (процедурное) программирование,
2. информационно-ориентированное программирование,
3. объектно-ориентированное программирование.

5
Третьим фактором является сложность и масштабность
задач, необходимость в реализации которых возникает в
практической жизни.
На рис. 1 приведена схема [1], отражающая хронологию и
влияние языков друг на друга. Ниже дается краткая характеристика
основных языковых групп, которые внесли наибольший вклад в
разработку систем программирования.
Группа языков FORTRAN
Ранние версии языка FORTRAN (1955 г.)обладали
следующими характеристиками:
- имена менее 6 символов;
- отсутствие операторов описания типов данных. Типы
данных присваивались по умолчанию (переменные, имена
которых начинаются с букв i, j, k, 1, m, n - всегда целого
типа, все остальные - вещественного);
- наличие только арифметического оператора условного
перехода;
- отсутствие раздельной компиляции подпрограмм;
- система типов включала только 4 базовых типа данных;
- массивы только статические, их размерность не превышала 3;
- цикл только типа пересчета (FOR...);
- практически отсутствовала проверка типов данных.
В языки FORTRAN И, FORTRAN IV были введены
следующие возможности:
- независимая компиляция подпрограмм;
- операторы описания типов;
- логический оператор IF.. .THEN;
- оператор описания общих областей COMMON;
- оператор EQUIVALENCE определял единую память для
нескольких переменных;
- довольно большая встроенная библиотека математических
функций.
В язык FORTRAN 77 введены:
- символьные строки;
- логический оператор условного перехода IF...
THEN...ELSE;
7
- циклы с условием (WHILE);
В языке FORTRAN 90 реализованы:
- динамические массивы;
- циклы с условием (WHILE);B языке FORTRAN 90
реализованы:
- динамические массивы;
- указатели;
- структурные типы данных;
- операторы CASE, CYCLE (переход на голову цикла без
выхода из него);
-под держка объектно-ориентированного программирования;
- имеется обширная встроенная библиотека по линейной
алгебре.
Группа алголоподобных языков.
Версия ALGOLa 1958 г. по сравнению с версией FORTRANa
обладала:
- операторами описания типов данных;
- составными операторами;
- вложенными операторами условного перехода логического
типа if.. .then... .else;
- массивами любой размерности.
В язык ALGOL-60 были введены:
- блочная структура программы
begin
var a,b,c;

end
- передача параметров в подпрограммы как по значению,
так и по ссылке;
- наличие рекурсивных подпрограмм;
- автоматические массивы.
ЯзыкALGOL-68 пополнился:
- динамическими массивами;
- указателями;
- введены некоторые типы, определяемые пользователем;
- оператор многовариантного ветвления switch.
Наследниками семейства языков ALGOL являются:
язык Pascal, обладающий такими качествами как простота,
выразительность, надежность; его объектно-ориенти-
рованный вариант Delphi; машинно-независимый язык С и
его потомки С++, Java, С#, поддерживающие абстрактные
типы данных и ООП.
Язык Ada (1983 г.) является продуктом мировоТо проекта
разработки языка программирования. Он создавался с целью стать
универсальным стандартом, взамен сотен других языков,
используемых в разных проектах. Язык Ada воплощает
большинство концепций, существовавших на конец 1970-х годов,
включая:
- пакеты как средство поддержки абстрактных типов данных;
- настраиваемые подпрограммы (т.е. типы переменных
заранее не определены, поэтому одной подпрограммой
можно обрабатывать информацию различных типов);
-наличие обширных средств обработки исключительных
ситуаций;
- возможность параллельного выполнения процессов,
оформленных в виде специальных блоков (заданий), на базе
механизма рандеву.
Ada 95 характеризуется:
- средствами разработки графического интерфейса
пользователя;
- поддержкой ООП;
- наличие обширных библиотек;
- усовершенствование механизмов параллельной обработки
данных посредством инкапсулированного задания.

1.5. Критерии оценки языков программирования.


С точки зрения влияния на разработку программного
обеспечения и его дальнейшей эксплуатации наиболее
характерными являются такие качества ЯП, как читабельность
программ, написанных на данном языке, легкость их создания,
надежность языка, стоимостные характеристики. Ниже
приводится описание критериев и их характеристик.
Таблица 1.
Критерии оценки
Характеристики
Читабельность Легкость
Надежност!
создания
1 .Ортогональность
/простота X X X
1.Управляющие
структуры X X X
3 .Типы и структуры
данных X X X
\. Синтаксическая
структура X X X
5.Поддержка
абстракции X X
б.Проверка типов X
7.0бработка
исключительных X
ситуаций
S.Ограниченное X
совмещение имен

Простота языка предполагает:


- разумное количество элементарных конструкций;
- разумное отсутствие множественности одних и тех же
свойств в реализации языка:
а=а+1;
а+=1;
а++;
++а.
- перегрузка операторов.
Ортогональность - возможность конструирования каких-
либо конструкций языка из элементарных (например, возможность
объявить новый пользовательский тип данных для описания
предметной области реализуемой задачи).
Управляющие структуры. Язык должен обладать разумным
многообразием управляющих структур.
Разнообразие типов и структур данных должно быть таким,
чтобы давать возможность адекватного описания предметной
области задач.
Продуманная синтаксическая структура программы
предполагает:
- длину имен объектов в программе такую, чтобы иметь
возможность отображать смысл объектов задачи;
- наличие зарезервированных специальные слова,
отображающих смысл конструкций языка;
- наличие продуманных символов составных операторов
(например, слова begin... end вместо {....});
- отсутствие множественного смысла одних и тех же
зарезервированных слов и операторов.
Поддержка абстракции.
Абстракция - возможность определять сложные объекты,
игнорируя детали. Абстракция в ЯП представляется двумя
понятиями: абстракция данных и абстракция процесса.
Под абстракцией процесса в ЯП понимается подпрограмма
(процедура или функция), поскольку она определяет способ, с
помощью которого программа может выполнить некоторый
процесс, без уточнения деталей того, как именно это следует
сделать (по крайней мере, в вызывающей программе).
Среди новых идей, возникших в области методологии
программирования и разработки языков программирования за
последние 25 лет, абстракция данных является одной из самых
значительных.
Абстракцию данных следует начать рассматривать с понятия
переменной, которая представляет собой абстракцию ячейки ОП.
При этом одной переменной определенного типа ставится в
соответствие одна ячейка соответствующего типа. Одновременно
оператор EQUIVALENCE в FORTRANe или Union в С, С++
позволяет в разные моменты времени в течение работы пограммы
загружать в одну и ту же ячейку несколько переменных различного
г

типа. Далее появилось понятие массива, который абстрагирует


область однотипных ячеек памяти. Затем возникла необходимость
описания разнотипных объектов одной структурой (например,
таблица данных разного типа), так в ЯП появился тип запись или
структура.
В языках объектно-ориентированного программирования
произошел синтез абстракции данных и абстракции процесса.
Появился абстрактный тип данных, который объединил данные
одного определенного типа и код обработки этих данных в единую
структуру. Переменные этого типа стали называться объектами.
Проверка типов.
Надежность языка предполагает наличие средств
обнаружения в программе всевозможных ошибок. При этом эти
средства могут функционировать как при компиляции, так и при
выполнении программы, и осуществляют:
- проверку типов (статическую, динамическую);
- проверку диапазона изменения индексов массивов;
- обработку исключительных ситуаций.
Средства обработки исключительных ситуаций позволяют
перехватывать ошибки и другие нештатные ситуации во время
выполнения программы, принимать меры и затем продолжать
работу. Этот механизм значительно повышает надежность
программ. Языки, как правило, обладают в большей (Ada, Java,
С++) или меньшей (С, FORTRAN) степени средствами такой
обработки, встроенными или пользовательскими.
Совмещение имен предполагает использование одной и той
же ячейки памяти под разными именами. Этот механизм является
источником ненадежности в программе, хотя и оправдывал себя в
условиях дефицита оперативной памяти Примерами совмещений
могут служить оператор EQUIVALENCE в языке FORTRAN или
объединение Union в языке С.
Стоимость языка складывается из:
1. Затрат на обучение программиста, что зависит от простоты и
ортогональности.
2. Стоимости создания программ на ЯП зависит от легкости его
использования. ЯП высокого уровня родилась из желания
уменьшить стоимость разработок.

12
3. Затрат на компиляцию.
4. Стоимости выполнения программы. Зависит от структуры ЯП,
например, проверки типов при выполнении очень дорогие.
5. Затрат на распространение ЯП и компиляторов для него: Java-
распространяется бесплатно и очень популярен.
6. Стоимости надежности - недопущения сбоя на важных
объектах.
7. Стоимости сопровождения программного обеспечения (ПО).
Язык программирования можно оценивать и с точки зрения
других классификаторов:
Мощность языка. Определяется тем разнообразием задач,
которые программируются на данном языке. Наиболее мощным
языком является машинный язык.
Уровень. ЯП делятся на:
- языки низкого уровня (машинные языки);
- ассемблеры, включающие мнемонические обозначения
команд;
- языки высокого уровня.
Мобильность языка. Определяется независимостью от
аппаратных средств.
Эффективность языка. Обеспечивает эффективную
реализацию языка (включая эффективную реализацию
компилятора и эффективные программы, генерируемые
компилятором).

1.6. Методы реализации ЯП.


Языки программирования могут реализовываться одним из
трех способов: посредством компиляции, интерпретаторов или с
помощь перевода текста программы на промежуточный язык, с
помощью которого процесс интерпретации идет быстрее.
Компиляция.
Лексический анализатор объединяет символы программы в
лексические единицы (лексемы) - идентификаторы. На основе
лексем синтаксический анализатор строит деревья синтакси-
ческого анализа, таким образом, определяя синтаксическую
структуру программы, сразу идентифицируя ошибки. Произ-
водится оптимизация программы путем уменьшения ее размера и
возможностей ускорения выполнения. Генератор кода переводит
оптимизированную программу в объектный код и определяет
семантику. Программа затем линкуется и переводится в загрузочный
модуль на машинном языке, который выполняется. Схема
компиляции приведена на рис.2.
Преимущества:
- компилируется сразу целиком модуль;
- после компиляции образуется выполняемый модуль, т.е.
обеспечивается мобильность программы.
Чистая интерпретация.
Программа-интерпретатор расшифровывает каждую
команду программы и сразу ее выполняет в процессоре.
Недостатки: скорость чистой интерпретации от 10 до 100
раз медленнее, чем компиляции.
Смешанные системы реализации.
Некоторые системы реализации ЯП представляют
компромисс: они транслируют программу на промежуточный
язык, разработанный для обеспечения более легкой интерпретации
(см. рис. 3). Примером может служить язык Java, реализованный
на основе смешанной реализации, программа преобразуется в
промежуточный язык - байтовый код.
Исходная
программа.

Результата
Рис.2. Система чистой компиляции
Исходная
программа

Рр.ч\ткгатм

Рис. 3. Система смешанной реализации


Глава 2. Объекты данных.
2.1. Переменные.
Переменная - это абстракция ячейки памяти, при этом ее
содержимое может меняться множество раз в процессе выпол-
нения программы.
Переменная имеет ряд характеристик:
- имя, т.е. идентификатор, под которым ячейка известна в
программе, при этом возникают проблемы: какова должна
быть длина имен, разрешить ли использование верхнего и
нижнего регистров и т.д..
- адрес или ссылка - определяет ту ячейку, которая связана
с переменной.
Существует три возможности связать переменную с адресом:
1 .одна ячейка - одно имя;
2.одна ячейка - несколько имен (например, тип
объединение Union в языке С);
З.одно имя - две ячейки (локальные переменные).
- значение переменной, т.е. содержимое связанной с ней
ячейки памяти;
- тип переменной - определяет множество значений и
множество операций над этими значениями;
- время жизни переменной, т.е. время, в течении которого
переменная связана с ячейкой;
- область видимости - определяется фрагментом программы,
т.е. теми операторами, в которых к переменной можно
обратиться.

2.2. Связывание
Связывание - это процесс установления связи между
объектом и его атрибутом (свойством).
В момент разработки программы связывание может
происходить:
- на этапе компиляции - статическое связывание, при этом
оно более надежное и занимает меньше времени;
- на этапе выполнения программы - динамическое связывание.
f^ll i • |se н и ш и й .
»|m .... вмш.пишия типа с переменной:
i4fi • . ,^juiiikiiiue.
I к i > # • «1нмс11цыо:
t Wisjmt M мьмпшения типов;
и|)мкн i мьыпшения типа, принятых при разработке языка.
),.. и пшишпю ЯП требуют явного объявления. Хотя в ЯП
h t|i и* VN, I'lЛ1, Бейсик есть неявные соглашения. Неявные
• >гч чмггпия способствуют возникновению ошибок. Статическое
мшмнание обычно реализуется через компиляторы, которые
но'шоляют легко транслировать программы в эффективные
машинные коды.
2. Динамическое связывание.
При динамическом связывании при объявлении тип не
указывается. Связывание происходит при присвоении переменной
значения оператором присваивания. Например, в ЯП APL,
SNOBOL4 может быть оператор
LIST <-10.3 4.6 16.7
который загрузит в переменную массив из 3-х реальных чисел. А
ниже оператор
LIST <-- 30
изменит тип переменной на целый.
Преимущества: обеспечение значительной гибкости
программирования. Одна и та же программа может обрабатывать
данные различных типов (пример: алгоритм обработки таблицы
данных, в которую один раз введены данные символьного типа;
другой - числового).
Недостатки:
1. В силу отсутствия статического контроля типов, может
случиться ситуация, когда неверные типы в правой части
оператора присваивания не могут быть обнаружены ни при
компиляции, ни при выполнении (пример: пусть х, у -целые
переменные в программе, а - массив, пусть в программе вместо
х = у по ошибке записали х = а, тогда тип х меняется на массив
и получаются неверные результаты). При статическом
связывании компилятор выдаст ошибку.
2. Сложная реализация, т.к. проверка типов, распределение памяти
делается только при выполнении; каждая переменная должна
содержать дескриптор для запоминания текущего типа; ячейка
для переменной должна быть динамического размера, поскольку
значения различных типов требуют различных объемов памяти..
3. Обычно реализуются через интерпретатор из-за сложности
динамического изменения типов в машинных кодах.
3. Логический вывод типа
Реализован в языке ML, который поддерживает и
функциональное, и императивное программирование. Он
использует механизм логического вывода типа, а именно: в
операторе
с=3.14*г*г
константа г определяет тип переменной с. Если константы нет, то
тип не определен.
2.2.2. Время жизни переменной.
Время жизни переменной - это время, в течении которого
переменная связана с ячейкой (адресом).
Размещение переменной в памяти предполагает:
- сопоставление реального адреса с именем переменной;
- занесение в эту ячейку конкретного значения.
Удаление из памяти предполагает открепление адреса от
ячейки.
С точки зрения времени жизни, переменные могут быть:
1. статические, связываются с ячейкой на стадии компиляции и
остаются связанными с той же ячейкой до конца выполнения
программы(глобальные).
Достоинства: эффективная прямая адресация; при выполнении
программы не затрачивается время на размещение и удаление
из памяти. Недостатки: уменьшается гибкость програм-
мирования; не поддерживаются рекурсии; невозможность
совместного использования одной и той же ячейки. В языках
FORTRAN1,2,4 все переменные были статическими. В С, С++
и Java есть специальный модификатор static для объявления
таких переменных
2. Динамические. Это безымянные ячейки - куча, размещаемые и
удаляемые с помощью явных команд периода выполнения,
определяемых пользователем. При этом связывание с памятью-
динамическое, связывание с типом - статическое. Обращаться к
таким переменным возможно только с помощью указателей и
ссылок.
Пример: С++, оператор распределения памяти new, его аргумент
- т и п переменной. Оператор удаления delete,
int intnode; /* intnode - указатель*/
intnode = new int /*связывает ячейку - помещает
адрес ячейки в указатель*/

delete intnode; /* освобождает ячейку */


Явные динамические переменные часто используются в таких
динамических структурах как связные списки и деревья,
которым необходимо расти и\или сокращаться во время
выполнения программы.
Недостатки: сложность корректного использования указателей
и ссылок.
3. Автоматические ("живут" внутри блока или процедуры,
размещаются в ОП, в стеке при обращении к процедуре и
удаляются из памяти при завершении работы). Связывание
автоматической переменной с типом происходит статически при
компиляции на основе оператора объявления. Связывание с
памятью - при выполнении программы. Позволяют
использовать рекурсии, т.к. рекурсивным процедурам требуется
некоторая локальная память, чтобы каждая активная копия
рекурсивной процедуры имела свою версию локальных
параметров.
Недостатки: затраты времени на размещение и удаление
переменных на стадии выполнения программы.
4. неявные динамические (переменные, размещение которых
происходит при загрузке в них конкретных значений).
Достоинства: высокая гибкость, позволяющая писать крайне
общие программы.

10
Недостатки: затраты на поддержку во время выполнения
программы всех динамических атрибутов, в том числе, типов,
диапазонов индексов массивов. Высокая ненадежность.

2.3.Тип переменной.
Определение типа, контроль типа, типизация языка - эти
вопросы определяют надежность ЯП.
Определение. Тип переменной (данных) определяет
(специфицирует):
1. Множество значений, которые может принимать переменная
данного типа;
2. Множество операций, определенных над объектами данного
типа;
3. Внутреннее представление данных и способ доступа к ним.
Следствие 1: Если дан новый тип, то можно описать и
инициировать, т.е. определить значение объекту этого типа.
Следствие 2: Если даны две переменные некоторого типа,
то их можно сравнить по крайней мере на равенство или
неравенство.
Следствие 3: Если два типа отличаются с точки зрения
указанных свойств, то они считаются различными.
Это определение говорит о том, что основа надежности ЯП
заключается в возможности проверить, что любая операция^
выполняемая над объектом данных соответствует
специфицируемому типу этого объекта.
Если два типа отличаются с точки зрения перечисленный:
свойств, то они считаются различными. Например, типы
Array[1..10] of real и Array[l..ll] of real
различны.

2.4. Контроль типов


Проверка! обеспечивающая анализ совместимости типов
операндов оператора, называется контролем типов. Она
заключается в определении типов выражений и их
согласованности с правилами типизации ЯП.
Согласованность типа обеспечивается:
- вручную (программист следит сам, чтобы типы были
согласованы);
- автоматически (с помощью функций преобразования типов
или операции приведения типа).
Операции приведения:
- суживающие - более широкий тип к более узкому(Яоа1 к
типу integer);
- расширяющие. Расширяющее преобразование почти всегда
безопасно. Проблема здесь может возникнуть только в
следующем. В некоторых реализациях целые числа имеют
размер 32 бита, что позволяет хранить 9 значащих
десятичных цифр. Если значения с плавающей точкой также
имеют размер 32 бита, то это 7 десятичных цифр. Поэтому
точность в 2 цифры потеряется.
Операции приведения могут реализовываться двумя
способами:
1. явный - через специальные функции (int(x), float(x), str(x) и
т.д.);
2. неявный - реализуется по правилам, заложенным при
разработке ЯП.
В теории языков нет единого мнения об использовании
приведения типов арифметических выражений. С одной стороны
повышается гибкость программ от введения всевозможных
приведений, с другой - приведение аннулирует выгоды от проверки
типов и тем самым уменьшает надежность.
Пример void main() {
int a,b,c ; float d;
a = b * d; /* надо было написать a=b*c */
язык С допускает смешанные выражения, поэтому компилятор
приведет b к типу float и ошибку никто не увидит.
В связи с этим в некоторых ЯП (АДА и Модула2) введено
ограничение на смешения операндов. В большинстве же языков
ограничений нет. Хотя наблюдение за приведением очень
затратный процесс, т.к. и поверка и преобразование происходит
при выполнении программы.
Контроль типов может выполняться:

112
- только при компиляции и, если все ошибки типов
выявляются при компиляции, то такой ЯП называется ЯП с
полным статическим контролем типов',
- только при выполнении - если все ошибки выявлены при
выполнении, то такой ЯП называется ЯП с полным
динамическим контролем типов',
- смешанный (ЯП со смешанным контролем типов).

2.5. Уровни типизации ЯП.


Правила типизации языка предполагают:
- наличие объявления типов данных;
- реализация связывания типов с переменной;
- определение типов смешанных арифметических
выражений;
- наличие правил преобразования типов.
Языки имеют несколько уровней типизации: ,
1. Слаботипизированный язык - язык, в котором
информация о типе используется только для обеспечения
корректности представления данных в ячейке.
с char;
с = 4; // ошибку не выведет
2. Сильнотипизированный язык - язык, который
осуществляет полный контроль типов (статический,
динамический или смешанный), в таком языке ошибки
типов выявляются все и всегда;
Сильная типизация обеспечивает высокую надежность.
Pascal -сильнотипизированный (за исключением
вариантных записей);
Fortran - слаботипизированный, т.к. в языке нет проверки
соответствия типов формальных и фактических параметров
подпрограмм, а также переменных операторов EQUIVALENCE.
Ada - почти сильнотипизированный, т.к. позволяет
программистам отключать проверку типов;
С - слаботипизированный, имеются некоторые функцйи,
параметры которых не подвергаются проверке, а также за счет
объединений..
2.6. Эквивалентность типов
Если реализуется оператор х:=<выражение>, то оно
реализуется только в том случае, когда типы операторов справа и
слева эквивалентны, т.е. необходимо определить правила
вычисления эквивалентности типов данных.
Существуют два способа определения эквивалентности
типов:
1. Эквивалентность (совместимость) по структуру или структурная
эквива-лентность. Две переменные имеют совместимые типы
в том случае, если у их типов одинаковые структуры.
2. Эквивалентность типов по имени или именная эквивалентность
с точки зрения типов, если имена их типов одинаковы (в
независимости от того, какова их структура и множество
значений).
Пример:
type
ТАБЛ1 array (1.. 10) of integer;
type
ТАБЛ2 array (1 ..10) of integer;
Var
X: array(l.. 10) of integer;
У-.ТАБЛ1, Z: ТАБЛ2;
В случае эквивалентности по структуре типы переменных
X, У, Z будут считаться одинаковыми, а в случае эквивалентности
по имени — различными.
С точки зрения удовлетворения правилам эквивалентности,
в языках вводится понятие производных типов и подтипов.
Производный тип строится на основе базового типа путем
определения именного, включающего логически связанные
объекты, задачи. Производный тип наследует значения и операции
базового типа.
Var
этот_возвраст, общий_возраст: integer;
i: integer;
begin
общий_возраст:=0;
while (i<=10) do
readln (этот_возраст);
общий_возраст:=общий_возраст+этот_возраст;
end.
Подтипы позволяют ограничивать множество значений и
множество операций базового типа.
subtype
возраст range [ 15... 3 0]: int;
рост range [150...210]:int;
индекс range [ 1... 100]: int;

2.7. Область видимости переменных.


Область видимости - фрагмент программы, в котором
переменная видима, т.е. определены ее атрибуты и к ней можно
обратиться. Область видимости тесно связана с понятием блока
программы.
Блок позволяет фрагменту программы иметь собственные
локальные переменные, которые, как правило, автоматические,
поэтому память им выделяется в начале выполнения блока и
освобождается по его окончании.
Существует два способа реализации области видимости: в
виде статического обзора данных и динамического обзора данных.
Статический обзор данных.
Статический обзор данных- это связывание нелокальных
переменных с атрибутами (т.е. определение области видимости
переменных) на стадии компиляции. Он создается определениями
главной программы, процедур и блоков и определяет механизм
вложенности процедур или блоков друг в друга (определяет
иерархию вложенности, см. рис. 4).
Когда в языке со статическим обзором данных компилятор
обнаруживает переменную, ее атрибуты определяются путем
поиска объявившего ее оператора.
Например:
Procedure big;
Varx: integer;
Procedure sub1;
begin {subl};
..x... end {subl};
Procedure sub2;
Var x: integer;
begin...end {sub2};
Begin {big}...end {big};
Ссылка на переменную x сделана в процедуре sub 1. Вначале
компилятор ищет объявление в subl, не находит, и продолжает
поиск в процедуре, породившей subl (статический родитель).

Г г
main
А

V.

Рис.4. Схема областей видимости.

Локализованные в блоке имена могут совпадать с ранее


объявленными глобальными переменными. В этом случае
считается, что локальное имя "закрывает" глобальное и делает
его недоступным.
Например:
Var
i: integer;
Procedure P;
Var
i:integer;
begin
writeln(i);
end(P);
BEGIN i:=l;
Обращение к блоку P Эта программа выдаст любое
значение, т.к. локальная переменная i "закроет" глобальную , а
значение i при входе в блок Р неопределенно. Если убрать описание
Var i:integer из блока Р, на экран будет выведено значение
глобальной переменной i, т.е. 1.
Недостатки:
1. Все переменные, объявленные в главной программе, видимы
во всех процедурах и избежать этого нельзя (только если закрыть
локальной).
2. Если в процедуре Е возникает необходимость видимости
переменной из процедуры D, то для этого надо переместить
процедуру D в процедуру Е. Но тогда будут нарушены области
видимости D и А.
Динамический обзор данных.
При динамическом обзоре данных видимость переменных
определяется последовательностью вызовов процедур, а не
структурой программы. Поэтому он всегда реализуется только при
выполнении программы.
Пример динамического обзора, т.е. все объявления перемен-
ных не были определены при компиляции.
Proc big;
Varx: integer;
Proc subl;
begin {subl};
..x... end {subl};
Proc sub2;
Var x: integer;
begin...end {sub2};
Begin {big}...end {big};
Значение переменной x определяется в зависимости от
последовательности вызова процедуры. При ссылке на перемен-
ную х во время выполнения программы начинается поиск среди
локальных переменных процедуры. Если объявления не нашли,
то обращаются не к породившей процедуре, а к процедуре,
вызывавшейся до этого (динамическому родителю) и используют
ее объявления.
Рассмотрим две последовательности вызова процедуры subl:
1) big --> sub2 --> subl
2) big »> subl --> sub2
При динамическом обзоре в 1) для переменной х действовать
будет объявление в процедуре sub2, в 2) будет использоваться
объявление в big.
При статическом же обзоре в обоих случаях используется
описание из процедуры big.

2.8. Среда ссылок


Средой ссылок оператора называется совокупность всех
имен, видимых в операторе. Среда ссылок при статическом обзоре
состоит из переменных:
1. его локальной области видимости
2. совокупности областей видимости его предков
Пример:
Program example;
Var a,b,: integer;

procedure subl;
var x,y: integer;
begin точка 1 end; {subl }
procedure sub2;
var x : integer;

procedure sub3;
var x,y: integer;
begin точка 2 end; {sub3 }
begin — точка 3 end; { sub2 }

begin .. точка 4 - end; {example }

В указанных точках программа имеет следующие среды


ссылок:
точка 1: переменные х и у процедуры sub 1, переменные а и
b программы example,
точка 2: х процедуры sub3 (х процедуры sub2 скрыта),
переменные а и b процедуры example,
точка 3: х процедуры sub2, переменные а и b программы
example,
точка 4: переменные а и b программы example.
Среда ссылок в языке с динамическим обзором состоит из
локально объявленных переменных и переменных всех других
активных на данный момент подпрограмм. При этом некоторые
переменные активных процедур могут быть скрыты от среды.
Новые активации процедур могут скрывать переменные в
предыдущих активациях.
Пример:
void subl() {
Int a, b ;
.... точка 1
}
void sub2() {
int b,c;
... точка2
subl;
}
voidmain() {
int c,d;
... точкаЗ
sub2; } . '
Вуказанных точках данная программа имеет следующие
среды сслылок:
точка I: переменные а и b процедуры subl, переменная с
процедуры sub2, переменная d функции main (переменная с функции
main и переменная b процедуры sub2 скрыты),
точка 2: b и с процедуры sub2, переменная d
функции main (переменная с функции main скрыта),
точка 3: переменные e n d функции main.

Глава 3. Система типов данных языка


Информация, которая обрабатывается ЭВМ может быть
числового, символьного или логического типов.
Сложность задач, моделируемых на ЯВУ, требует различных
способов представления разнообразных данных.
Отсюда следует, что система типов данных ЯП должна:
1. Поддерживать базовые(элементарные, простые, предоп-
ределенные) типы, а именно:
- целый (integer),
- плавающий (real),
- логический (boolean),
- символьный (character).
2. Поддерживать высокую ортогональность, т.е. и давать
возможность создавать адекватное множество типов для
описания предметной области задачи.

3.1. Целый тип данных.


Целые числа представляются в компьютере в виде строки
битов, причем крайний слева бит - знак числа. Целые типы
под держиваются непосредственно аппаратурой.
Сейчас как правила компьютеры поддерживают целые числа
нескольких размеров (различных диапазонов), что нашло
отражение в ЯП: имеются короткий, целый, длинный, беззнаковый
форматы.
Операции: сложение (+), вычитание (-), умножение (*),
деление (/), остаток от деления (mod), возведение в степень (**),
сравнение на равенство (=), сравнение на неравенство (/=),
сравнение на меньше (<), сравнение на меньше или равно (<=),
сравнение на больше (>), сравнение на больше или равно (>=),
унарный плюс (+), унарный минус (-), абсолютное значение (abs).

30
Поясним семантику операции / и mod. Результат операции
целения всегда округляется (т.е. целый). Важно, в какую сторону,
производится округление. При делении положительных чисел
общепринятой практикой (как в математике, так и при машинной
реализации) является округление в сторону нуля. Однако, в
Ьолыиинстве ЭВМ, когда один из операндов отрицательный,
результат также округляется в сторону нуля, что уже отличается
от математического понятия целой части. Здесь возможны четыре
решения:
- включить проверку знака и сделать корректировку для
отрицательных операндов;
- запретить отрицательные операнды;
- ввести две различные операции, одна из которых округляет
к пулю, а другая к отрицательной бесконечности;
оставить так, как есть.

3.2. Вещественный тип


Вещественный тип, т.е. тип чисел плавающей запятой
моделируют действи-тельные числа., хотя их представления
являются аппроксимацией, т.к. ни одно из них не может быть точно
представлено в виде числа с плавающей точкой (см. рис. 5 ).

Мантисса 23 бита(52) 31
Знак числа

Знак порядк; Порядок


8 бит(11)

Рис. 5. Внутреннее представление числа


с плавающей запятой

Большинство ЯП содержат два типа: FLOAT (RERAL) -


стандартный размер, обычно в 4 байта памяти; и DOUBLE -
мантисса занимает в 2 раза больше бит памяти.
Константы действительного типа имеют три вида предоп-
ределенных имен:
запись с точкой: 10.4, -0.000000000145
запись с порядком: 104Е-01 , -145Е-12
запись с точкой и порядком: 1.04Е+01,-1.45Е-10
Основные операции: сложение (+), вычитание (-), умножение
(•), деление (/), возведение в степень (**), сравнение на равенство
(=), сравнение на неравенство (/=), сравнение на меньше (<),
сравнение на меньше или равно
(<=), сравнение на больше (>), сравнение на больше или
равно (>=), унарный плюс (+), унарный минус (-), абсолютное
значение (abs).
Однако при обработке действительных чисел с конечной
точностью представления следует учитывать возможные ошибки
округления (особенно при сравнении их на равенство и неравен-
ство). Поэтому следует сравнивать действительные числа лишь
на приближенное равенство и неравенство, например
if abs(x-y)<0.00001then ...
Кроме этого, при вычислении выражения с действи-
тельными числами возникают погрешности, которые могут оказать
существенное влияние на получаемые результаты. Следующие
рекомендации позволяют уменьшить погрешность вычислений:
• при сложении ряда чисел определять все положительные
и отрицательные слагаемые и складывать их поочередно;
• складывать наименьшие члены сумм первыми;
• избегать вычитания двух почти равных чисел; если это
все же необходимо, производить вычитание до умножения
или деления (т.е. лучше записать а*(Ь-с), чем а*Ь-а*с);
• избегать показателей степени в форме действительных
чисел, так как в этом случае степень вычисляется через log
и ехр; напротив, х**2 вычисляется как х*х;
• использовать операцию sqrt(x) при вычислении х0 5, так
как sqrt обычно вычисляется точнее;
• использовать в процессе вычисления максимально
возможную точность, если это необходимо;

32
• избегать цепочек операций, в которых используются
неточные значения;
• использовать алгоритмы, для которых известны оценки и
границы ошибок.

3.3. Числа с фиксированной запятой.


Большинство компьютеров, разработанных для
коммерческих приложений, содержат аппаратное обеспечение,
поддерживающее типы десятичных чисел FIXED . Числа типа
FIXED представляются в форме с фиксированной точкой, которая
стоит в установленном месте и отделяет целую часть от дробной
части (простые дроби). Такие типы данных в коммерческих
приложениях и ЯП КОБОЛ являются основными.
Достоинства: позволяет осуществлять точную арифметику
в установленном диапазоне.

3.4. Логический тип.


Впервые они появились в ЯП АЛГОЛ 60 и с 1960 г. были
включены во все универсальные ЯП, кроме С.(в С истина - это
любое число > 0, ложь - это 0).
Три стандартные логические операции применяются к
операндам логического типа, давая в результате логическое
значение: логическое дополнение (not), логическое И (and),
логическое ИЛИ (or). Основное назначение логического типа
состоит в реализации условий для условного оператора и оператора
цикла.
Диапазон значения: true или false. Значения представляются
в машине минимальной ячейкой памяти (байт). Основные
операции: not-логическое дополнение, and-логическое "и", ог-
логическое "или". Используется в операторах условного перехода,
операторах цикла, для организации переключателей.

3.5.Строковый тип (string).


Символьные строки - это последовательности символов.
В С, С++ строки вводятся как символьные массивы типа
cahr. Набор операций над такими массивами вызывается из
стандартной библиотеки string.h. Строки символов здесь
завершаются спец. символом: нулевым байтом - 0. Этот 0 заносится
автоматом при создании строки символов. В ЯП Paskal (с версии
5.0), delphi, Ada, Fortran77, 90, Бэйсик, Java тип string является
встроенным.
Существует несколько проектных решений, касающихся
длины строковых величин.
1. Статическая реализация.
Длина может быть статической и задаваться в объявлении:
Var str : string[15];
Строки со статической длиной всегда полные: если
строковой переменной присвоили строку меньшей длины, то
свободные места забиваются нулями.

м а м а 0 0 0 0 0 0
1 2 10

Дескриптор такой строки имеет следующий вид:

_str
длина
адрес

2.Строки переменной длины с ограничением.


Текущая длина при этом ограничивается специальным
символом, в С и С++ ставится нулевой байт.

>1 а м а /0
1 2 10

Такая строка имеет следующий дескриптор :


str
общая длина
текущая длина
Адрес
3. Строки с переменной неограниченной длиной
(SNOBOL4, Perl). Такая реализация дорога по времени на
размещение и удаление из памяти, но обеспечивает гибкость..
Основные операции: обращения к подстрокам,
конкатенации, операторы отношений и присваивания, определение
длины строки, сравнение строк.

3.6. Порядковые типы,


определяемые пользователем. *'
3.6.1. Перечислимый тип.
Этот тип данных имеет следующие характеристики :
- задается набором значений, которые он содержит.
- в качестве значений используются строковые константы.
- внутреннее представление совпадает с типом byte (0.. .255).
Формат описания:
type А = (красный, синий, зеленый);
Операции: равенства, определение порядкового номера
элемента, другие операции отношения вводятся не всегда.
Всегда реализуются операции-функции:
- определение номера по значению;
- определение значения по номеру;
- определение последующего (предыдущего) значения;
- определение min/max значений.
Для того, чтобы описать в другом типе ту же константу,
нужно указывать имя типа перед значением константы.
3.6.2. Ограниченный тип
Ограниченным типом называется непрерывная последо-
вательность порядкового типа, которая задается минимальным и
максимальным значениями из последовательности (в языке Pascal
такой тип называется тип-диапазон).
Формат
Type
<имя rana>=<min>.. .<max>;
Пример:
type
Т1 = '0', ' Г , ... '9',
Type
Т2 = 0,1, ...9;
Этот тип наследует операции своего базового типа. При
реализации такого типа компилятор должен выполнять проверку
выхода значений за границу допустимого диапазона типа.

3.7. Структурные типы.


Любой структурный тип представляет собой абстракцию
области памяти, всегда имеет внутреннюю организацию и эта
структура для каждого типа различна.

3.7.1. Массивы
Массив - это поименованная область ОП, ячейки в области
перенумерованы и являются одного типа. Элемент массива -
индексированная переменная,
var
<имя> : аггау[нач.зн. .. кон.зн.] of <тип>;
Тип массива включает в себя тип элемента и тип индекса.
Обращение к каждому элементу массива осуществляется по
общему имени и индексу элемента: A[i]
Категории массивов.
Массивы делятся на 4 категории, которые определяют
способ распределения памяти под массивы.
1. Статический массив.
Это массив, в котором диапазоны значений индексов
связываются статически и размещение в памяти также статическое.
А : array[ 1.. 100] of integer;
2. Фиксированный автоматический массив.
Диапазон значений связывается в статике, а размещение в
памяти происходит динамически, во время выполнения.
const п=10, ш=100;
procedure SSS(A(n,m));
3. Автоматический массив.
Размещение в памяти происходит динамически. Но после
связывания индексов и размещения в памяти и диапазоны, и адрес
памяти массива не изменяется в течении всей жизни переменной.
Var
procedure SSS (A(n,m));

read(m,n);
4. Динамический массив.
Это массив, в котором связывание индексов происходит
только при выполнении программы для чего служат специальные
функции. При этом ячейки памяти, содержащие массив могут
меняться сколько угодно раз.
<имя массива>=пе\у <тип>[размерность];
Операции над массивами.
Операцией над массивом называется действие, при
выполнении которого массив считается единым целым.
В языке Fortran 90 операции линейной алгебры над
массивами реализованы как встроенные функции (бперации с
матрицами, векторами).
В языке Pascal возможны операции присваивания:
А, В : array[1..10] of real;
А:=В;
В языке Ada реализованы:
- присваивание;
- операции отношения;
- конкатенация (сцепление).
Операция инициализации массива - это заполнение его
начальными значениями.
В языке Pascal инициализация не допускается.
В языке Fortran для этого служит специальный оператор:
INTEGER А(3), В(3);
DATA AS1,5,7SBS2..4S;
В языке Ada инициализация осуществляется следующим
образом:
А:аггау(1..4) of int==(l,2,3,4);
A:array(1..4) of int (1>=3,2>=4,other >=0);
В языке С:
int a[3]= {1,5,3};
int a[3][5]= {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
или
int a[3] [5]= {1,2,3,4,5},{6,7,8,9,10},{11,12,13,14,15}};
Если инициализаторов меньше, то оставшиеся элементы
массива не определяются. В этих языках есть особенность -
компилятор сам может устанавливать длину массива на основании
инициализатора: int list [ ] = { 4,6,10,7 }.
В некоторых языках есть операция сечения, т.е. выделение
из структуры массива некоторой части.
Сечением называется некоторая подструктура массива. Если
М - матрица, то сечение, например, строка или столбец. Сечение
не является новым типом, это механизм обращения к части
массива.
Массивы: vector (1..10), mat(l:3,l:3)
Сечения: vector(3:6),
Mat(l:3,2) 2-й столбец матрицы
Mat(3, 1:3) 3-я строка матрицы
Сечения могут быть и нерегулярными: vector ((/3, 2, 1, 8/))
это массив, содержащий 3, 2, 1 и 8 элементы массива vector.
Реализация массивов.
Команды, осуществляющие доступ к элементам массива
формируются при компиляции. Эти команды вычисляют адреса
элементов массива.
1. Для одномерного массива функция доступа к адресу
элемента следующая:
Адрес(массив(к))=адрес(массив(1)+(к-1))* (размер
элемента)=адрес(массив(1)) - (размер элемента) + (к*размер
элемента)
Дескриптор одномерного массива:
Имя
тип элемента
тип индекса
начальный индекс
конечный индекс
адрес 1-ого элемента

Пример. Пусть размер элемента 4 байта.


Адрес(А(5))=0+4*4 байта = 16-й байт.
2. Двумерный массив.
Такой массив в памяти хранится либо по строкам (С, Pascal),
либо по столбцам (Fortran).
Адрес(А(у))=адрес(А(1 ^))+((число строк над 1-ой)*размер
строки + число элементов j - o r o ) ) * ( p a 3 M e p элемента)
Например, Адрес(А(3,2))=28б. •
Ассоциативные массивы
Это массив с неупорядоченным множеством элементов,
индексированных таким же количеством величин, называемых
ключами.
Ключи должны содержаться в структуре массива, т.е.
каждый элемент здесь является парой - ключом и величиной.
Такие массивы используются в Perl и Java.
В Perl они называются хэшами, создаются и удаляются с
помощью функций хэширования. Хэшированные переменные
содержат знак %.
%salaries = ("Васу" =>3600, "Petr" => 42000, "Masha"
=>6000);
Обращение к элементу производится через знак $:
$ salaries {"Васу"} = 70000;
Удаление из хэша : delete $ salaries {"Васу"};
Размер хэша всегда динамический.
3.7.2. Множества.
Переменная множественного типа содержит неупоря-
доченную совокупность отдельных величин, которые имеют
некоторый порядковый тип.
type
<имя THna>=set о^значения базового перечислимого
порядкового типа>;
Пример,
type
Tl=set of'0'..'9';
T2=setof0..9;
Т3=(красный, сильный, зеленый);
T4=set of ТЗ;
Var
si, s2, s3:Tl;
s4,s5,s6:T2;
s7:T4;
begin
sl=['l','2','3'];
s2=['3','2','1'];
s3=['2', '3'];
s4=[0..3, 6];
s5=[4, 5];
s6=[3..9];
87=[синий, зеленый]; end/
Реализация множеств осуществляется битами машинного
слова, т.к. величина машинного слова разная, то возникает
проблема переноса программного множества на другую машину.
Мощность множества определяется величиной машинного
слова.
Операции:
- Умножение- пересечение двух множеств
s4*s6=[3,6];
- Объединение.
s4+s5=[0..6];
- Разностьопределяет элементы 1-ого множества, которые
не принадлежат 2-ому.
s6-s5=[3, 6, 7, 8, 9];
- Эквивалентность ( = ). Две переменные множественного
типа эквивалентны, если все их элементы одинаковы,
причем порядок следования безразличен (результат
операции есть true, если множества эквивалентны).
- Неэквивалентность (<, >).
Результат операции есть true, если множества не
эквивалентны.
- Включение (<= ), дает true, если первое множество
включено во 2-ое.
s5<=s6;
- Принадлежность элемента (in).
Результат true, если элемент или выражение принадлежит
множеству. 4 in s6
1 in s6

3.7.3.3апись
Запись - это неоднородная с точки зрения типа совокупность
данных, в которой отдельные элементы идентифицируются
именами. Каждый элемент называется полем записи (первые
записи были реализованы в языке Cobol).
Синтаксис записи следующий:
type <имя>=гесогё;
<имя поля 1>.<тип>;

<имя поля п>.<тип>;


end;
Здесь <тип> - любой допустимый тип данных.
Обращение к элементу:
<имя переменной>.<имя поля>;
Если при обращении к полю записи указываются имена всех
уровней иерархии, то это называется полностью определенной
ссылкой на поле; если все или некоторые имена иерархии опущены -
эллиптической ссылкой.
Эллиптическая ссылка используется в операторах with:
type student=record;
fio:string;
ball:int;
end;
Var b : student;
Begin
with b do
йо:='Иванов';
ball:= 5;
end.
Операции над записями:
1. Сравнение на равенство и неравенство.
2. Копирование полей: из записи источника - в запись результат.
3. Над полями записей определены все операции, присущие
объявленному типу.
Запись имеет следующий дескриптор:

Имя записи
Имя поля 1
Тип поля 1
Смещение поля 1

Имя поля к
Тип поля к
Смещение поля к
Адрес

В языке С существуют специальные записи, которые


называются битовые поля. Их формат следующий:
struct имя {
<тип> <имя поля 1> : длина в битах;

<тип> <имя поля п> : длина в битах;


}

42
Свойства битовых полей:
1. Если длина поля превышает int, то добавляется второй int.
2. Если длина поля ложится на границу, то это поле смещается в
начало следующего поля.
"}. В одной структуре могут быть как обычные поля, так и битовые.
Пример,
struct s{
int pi : 5;
p2:6;
: 2; IIт.к. поле без имени, то 26 пропускаются
}•

3.7.4. Объединения.
Объединение - ячейка ОП, которая в разные моменты
времени может хранить информацию разного типа.
Реализуются в Fortran в виде оператора EQUIVALENCE:
INTEGER имя1
REAL имя2
EQUIVALENCE (имя1, имя2);
В языке С это объединения Union:
union u {
int i;
char c;
float r;
}•
Под объединенную ячейку память всегда выделяется по
максимальной длине.
Для переменной типа union определены операции сравнения
на равенство и неравенство. Это довольно ненадежный тип
данных, т.к. проверку типа в объединении можно осуществить
только на стадии выполнения программы, что требует больших
временных затрат.
Вариантная запись.
С 5-ой версии языка Pascal в нем декларированы разме-
ченные объединения, которые в Pascal реализованы как

43
вариантные записи. Появилась метка (переменная), которая хранит
тип варианта. Это позволило осуществить статическую проверку
варианта.
Туре гес2 =r ecord
с : longint;
Cast ааа byte of
1 : (d : word);
2 : (e : single)
end
Варианты оформляются конструкцией Case ... of и
примечательны тем, что память под них отводится одна и та же
(как в объединениях).
ааа e(d)
Наличие вариантных записей в Pascal позволило обойти
отсутствие блоков в Pascal (блок позволяет объявлять новые
переменные в теле программы).
Program
type
rec: record;
с : longint;
case x : byte of
1: (d : word);
2: (c : record);
case boolean of
3 : (f: int);
3 : (g : real);
'3' : (c : word);
end.
Var
r : rec;
begin
r.e.c.=1000;
end

44
3.7.5. Указатели.
Указатель - переменная, значением которой может быть
адрес ячейки памяти или особый символ 0 - нулевой адрес, который
используется как пометка того, что указатель свободен.
Описание указателей:
в языке Paskal в языке С
Var char *ch;
ij:Ainteger; int*i, *j;
r^eal; float *r;
ii: int; int ii;
Указатели применяются в следующих случаях:
1) Для организации косвенной адресации.
2) Для организации обращения к ячейкам динамической
памяти (кучи).Переменные, размещенные в куче, не имеют имен,
ссылаться на них можно только с промощью указателей и ссылок.
Тип указателя определяется типом переменной, на которую
он указывает.Указатель, в котором объявлен конкретный тип
называется типизированным.
В языке Pascal существуют и нетипизированные указатели
Var
p:pointer;
Операции над указателями:
1. Инициализация указателя - присваивание адреса указателю,
инициализация указателя адресом может происходить в
следующих ситуациях:
- Присваивание динамического адреса. По запросу прог-
раммы система отводит блок ячеек динамической памяти
(кучи) и помещает адрес начальной ячейки в указатель i,
т.е. установливается связь между кучей и указателем. Для
такого механизма используется специальная функция:
n e w ( i ) ; malloc(j);
- Посредством операции взятия адреса:
i=&ii;
- Обеспечение косвенной адресации для нединамического
объекта, например, массива:
int a[10];
j = &a[0] // помещаем в j адрес a[0]
тогда j + 1 будет определять адрес элемента а[1].
В языке С указатели широко используются при обращении
к массивам, строкам, структурам, функциям.
2. Разыменование указателя - взятие (присваивание, помещение)
значения из ячейки, на которую указывает указатель. Если
указатель i указывает на ячейку, которая содержит число 206,
то оператор
ii = *i; // <— 206
поместит это число в переменную ii.
3. Освобождение динамической памяти. Освободить память это
значит отдать ячейки памяти в распоряжение системы с
помощью функций
dispose(i); free(i);
Следует заметить, что эти функции не освобождают указатель,
они только возвращают переменные в кучу.
4. Освобождение указателя - помещение в него нулевого адреса.
const с : pointer = NIL //нулевой адрес в Pascal

dispose(i);
i = c;
В некоторых языках операции 3 и 4 совмещены, но они
имеют разную структуру. Только после того, как в указатель
занесли 0, его можно использовать для другого адреса.
5. Присваивание.
Операцию присваивания можно реализовывать только для
однотипных указателей.
Var
pl,p2 : Aint;
рЗ : Агеа1;
рр : pointer;
begin
pl=p2;
pi = рЗ; //ошибка, чтобы ее обойти, надо использовать
указатель рр
J
рр = рЗ;
pi = pp; end.
6. Арифметика над указателями:
операция "+":
int *pl;
pl=2000;
р 1 =р 1 +3 //поместит в р 1 адрес 2012
операция "-":
int *pl, *р2, а;
а=р2-р 1; //смещение р2 относительно р 1
6. Операции отношений: >, <, =, <=, >=. , ».
При использовании указателей возникают две основные
проблемы.
1. Висячий указатель - указатель, содержащий адрес динамической
переменной, которая уже возвращена в кучу (удалена из
доступной памяти). Они создаются в следующей ситуации:
- пусть pi указывает на некоторую ячейку
- р2 = pi; //присваиваем р2 значение pi
- возвращаем ячейку в кучу и освобождаем pi.
Тогда указатель р2 будет содержать адрес свободной ячейки.
Такая ситуация может привести к следующим последствиям:
а), в эту ячейку может загрузиться информация другой
программы или операционной системы, б), через указатель р2
мы имеем доступ к «чужой» информации.
2. Потерянные переменные - это ячейки размещенные в
динамической памяти и переставшие быть доступными для
пользовательской программы. Такие переменные называются
мусором. Они создаются в случае, если:
- целью указателя pi устанавливается одна динамическая
переменная
- затем целью того же pi устанавливается другая динами-
ческая переменная. Тогда первая ячейка становится
недоступной для программы, т.к. связь с ней теряется.
Эта проблема называется еще утечкой памяти и затрагивает ЯП,
в которых требуется явное удаление из памяти переменных. В
некоторых языках организована автоматическая сборка такого
мусора, например, в языке Java.

47
Литература
1. Р. У. Себеста. Основные концепции языков программи-
рования, 5-е изд.: Пер. с англ., М., Издательский дом "Вильяме",
2001, стр.672.
6. В.В. Фаронов. Программирование на персональных ЭВМ
в среде Турбо-Паскаль, М.: Изд-во МГТУ, 1992, стр. 448.
7. Б.И. Березин, С.Б. Березин. Начальный курс С и С++, М.:
ДИАЛОГ-МИФИ, 1996, стр. 288.
8. А.Г.Калинин, И.В.Мацкевич. Универсальные языки
программирования. Семантический подход. М.: Радио и связь,
1991.
9. Т.Пратт. Языки программирования: разработка и
реализация. М.: Мир, 1979.
Содержание

Глава 1. Оценочные характеристики 3


языков программирования 3
1.1. Введение 3
1.2. Категории языков программирования
1.3. Классификация применений
языков программирования 5
1.4. Характеристики основных языков 5
1.5. Критерии оценки языков программирования 9
1.6. Методы реализации ЯП 13
Глава 2. Объекты данных 17
2.1. Переменные 17
2.2. Связывание 17
2.2.1. Связывание типов с переменной 18
2.2.2. Время жизни переменной 19
2.3.Тип переменной 21
2.4. Контроль типов 21
2.5. Уровни типизации ЯП 23
2.6. Эквивалентность типов 24
2.7. Область видимости переменных 25
2.8. Среда ссылок 28
Глава 3. Система типов данных языка 30
3.1. Целый тип данных 30
3.2. Вещественный тип 31
3.3. Числа с фиксированной запятой 33
3.4. Логический тип 33
3.5.Строковый тип (string ) 34
3.6. Порядковые типы, определяемые пользователем 35
3.6.1. Перечислимый тип 35
3.6.2. Ограниченный тип 36
3.7. Структурные типы 36
3.7.1. Массивы 36
3.7.2. Множества 40
3.7.3.Запись '41
3.7.4. Объединения 43
3.7.5. Указатели 45
Литература 48

Вам также может понравиться