лекции (2004) (1160823), страница 9
Текст из файла (страница 9)
Х:Т;
Х1:Т1; // лежит в диапазонеL1..R1
Х2:Т2;
Х3:Т3;// лежит в диапазонеL3..R3
Х=Х1; // допустимо
Х1=Х3; //формально допустимо, однако компилятор автоматически проверяет, попадает ли соответствующие переменные в соответствующий диапазон
Но совместимы ли между собой D, E и F? По документации языка Ада они совместимы по присваиванию, только если они имеют одинаковую длину. Таким образом, совместимы только D и E.
X1:D;
X2:E;
X3:F;
X1 := X2;
X2 := X3; // ошибка!
Вернемся к обсуждению неограниченного типа, переменные неограниченного типа объявлять нельзя:
Y:B; // ошибка, потому что компилятор не может произвести распределение памяти, здесь нет диапазона индексов
Y:B range -1..9; // можно
Будет ли переменная У совместим с Х1 или Х2? Заметим, что У, Х1 и Х2 принадлежат одному и тому же типу В. Y принадлежит анонимному подтипу типа B, следовательно, он будет совместим с типами D и E, так как длина объекта 11 элементов. Неограниченные типы можно использовать для введения новых подтипов, но это неосновное их назначение.
Неограниченные типы могут использоваться для формальных параметров процедур.
function SUM(X:B) return T; Мы можем обращаться к данной функции и с Х1, и сХ2, и сХ3, и с У, так как все они объекты типа В.
Для каждого массива определены специальные атрибутные функции. Если Х объект регулярного типа (массив), то для него определены атрибуты:
X' LENGTH длина
X' LOW индекс первого элемента
X' HIGH индекс последнего элемента
X' RANGE диапазон от X' LOW до X' HIGH
Если Х просто объект данных, то все элементы статические, если Х формальный параметр процедуры или функции, то эти атрибуты динамические.
function SUM(X:B) return T;
for i in X'RANGE loop
S:=S+X(i);
end loop
return S;
Таким образом, все объекты данных у нас имеют статические атрибуты и проблема эффективности решена, а проблема гибкости решена за счет того, что у неограниченных объектов все атрибуты динамические.
Если речь идет о многомерных массивах, то у каждого атрибута добавляется параметр i, который обозначает номер измерения массива (от 1 до к, где к – число измерений, не путать с длиной). В одномерном случае – единичка, которую можно опускать.
Оберон.
В языке Оберон (минимальном ЯП) продолжен подход языка Модула 2, а именно: если объект данных, то все характеристики статические, а если массив, то границы от 0 до N-1. Отсутствует понятие перечислимого тд, и понятие диапазона. Все массивы начинаются с 0 и заканчиваются N-1, есть понятие открытого массива.
ARRAY N OF T; // N – длина массива
ARRAY OF T; // открытый массив
В Обероне2 появились многомерные открытые массивы, есть функция High(x[i]) – дает индекс крайнего элемента по каждому из измерений, i меняется от 1 до к, где к – количество измерений.
Современные языки.
В языках Java, C# и Delphi массивы - частные случаи объектов. Элементы простых типов данных также являются частными случаями объектов, есть специальные классы, которые представляют эти типы данных как объекты. Объекты соответствующих классов - только объекты из динамической памяти, как следствие массивы являются объектами из динамической памяти. Диапазон индексов – диапазон целочисленный от 0 до N-1, поэтому для задания длины и границ диапазона необходимо задать только количество элементов. Поскольку место под массивы отводится в динамической памяти, то и атрибут N является чисто динамическим. Есть понятие только чистого динамического массива.
Объявление массива:
int [ ] a;// [ ] – указывают на то, что это массив
а – с одной стороны имя массива, а с другой только ссылка на объект.
a = new int [10]; //Только после этого массив начинает существовать.
У всех массивов есть методы a.length – показывает длину массива, a.setlength – позволяет установить новую длину массива.
Языки программирования. Лекция 9
Составные ТД( продолжение)
Массивы
Основные отличия в реализации массивов – насколько могут быть динамичными:
- базовые типы элементов ( связываются с объектом данных в объявлении во всех яп)
- границы диапазона
- длина
В языке Ада есть понятие "динамический" массива (на самом деле он квазистатический, т.е. он не является неограниченным, но у него границы - динамические атрибуты) . Объекты данных могут появляться только внутри блоков.
X:A range 0..20
Т.о. можно написать
PROCEDURE P(N:INTEGER)
X: array (1..N) of Real;
....
begin
end P;
Менять свои размеры внутри функции массив X не может, но он переменный (зависит от параметра функции).
В современных языках массивы:
-
границы от 0 и являются неотрицательными индексами;
-
длина- исключительно динамическая;
-
переменная типа массив- динамическая;
Ссылки
В языке Java все тд делятся на:
- Простые
- Референциальные (доступ к ним осуществляется только по ссылке) - это массивы, классы и интерфейсы .
В С# тоже самое + структурные типы данных( кроме простых).
Для референциальных тд (Cи#, Java, Delphi и т.д.):
- объекты отводятся только в куче
-имя для объектов является ссылкой: если в
X:I - I - объект, то Х - ссылка на такой объект.
Массивы:
C x;
int [] a;
a = new int [10];
Присваивание:
T[] a;
T[] b;
b = new T [100];
a = b; // копирование ссылок, а не массивов!
Длина- динамический атрибут.
Для изменения длины массива используется метод a.SetLength().
С точки зрения производительности: реализация таких массивов немного менее эффективна, но, т.к. все объекты находятся в ОП, накладными расходами можно пренебречь.
Вопрос вырезки из массивов (получения подмножества элементов массива).
В языке фортран 90 самые “ богатые” вырезки.
A[50][50];
A[1][*]; - Первая строка матрицы
A[*][3]; - Третий столбец матрицы
А[3][5..10]- вырезка
Таким образом, мы можем получить произвольный прямоугольник матрицы.
Из относительно современных ЯП такое понятие вырезки есть только языке Ада:
array (1..20, 1..30) of T
A(2..6, 3..4)- только смежные вырезки!
В современных языках понятие вырезки отсутствует из-за неэффективности,
В С++ перенесено в библиотеку STL- ValArray. Тенденция оформлять технологические конструкции, полезные ограниченной группе пользователей , в стандартные библиотеки.
Многомерные массивы
Многомерный массив в конечном итоге сводится к одномерному вектору
A[50][60]
Быстрее меняется правый индекс (если вытянуть этот массив в строчку).
C#, Java:
int [ , ] a; - двумерный массив ( границы –динамические атрибуты)
a = new int [3][4]; - инициализация массива.
int[] b = {1, 2, 3};- одномерный массив( отведение памяти)
int[,] c = {{1, 2, 3}, {3, 4, 5}, {6, 7, 8}}; -двумерный массив ( отведение памяти)
Причем массив только прямоугольный.
В языке C# появился новый тип массивов - Jagged-массивы ("ступенчатые массивы" - вектора из указателей на другие массивы).
int [][] a;- двумерный ступенчатый массив
Инициализация
a = new int[][4];- память под 4 элемента
a[0] = new int [3];
a[1] = new int [8];
a[2] = new int [0];- ссылка на массив длины 0!
Подмассивы имеют произвольную длину.
Можно моделировать треугольные массивы, разреженные матрицы и т.д.
Jagged-массивы делают реализацию подобных массивов наиболее эффективно.
В С++ можно воспользоваться классами для реализации подобных массивов.
Записи
Запись - агрегат из разнотипных записей.
T1xT2x...xTn
Для записей применим операция точка - доступ к полям записи.
R.имя_поля
Если бы операцию точка можно было бы перекрыть в С, то она возвращала бы ссылку.
Какое связывание у этой операции: статическое или динамическое?
Статическое - .имя_поля в языке ассемблера транслируется в смещение адреса.
В языке JavaScript подобного рода операция имеет динамическое связывание! Поскольку каждый объект данных в нём имеет тип класса, у которого есть свойства, т.о. если при выполнении операции R.имя_поля имени поля нет, то оно создаётся, если существует - то вычисляем доступ.
Т.о. точка:
-
доступ
-
создание свойства
5.new_prop = 1 - добавляем к целому типу данных новое свойство.
Запись вида R.имя тождественна R["имя"]
-
можно вычислять адреса полей,
-
разница между массивом и записью пропадает (можно добавлять свойства)
Такое осуществляется при существенной потери эффективности, которая JavaScript и не нужна.
Из соображений эффективности в современных языках операция точка - статическая.
В остальном записи в современных ЯП замещены понятием класса.
Структуры
Чем структура Си отличается от структуры Си++?
Тэги:
struct S {...};
union S;-объединение
enum S;- перечисление
Характеризуются
-имя подчиняется общим правилам идентификаторов
-образуют собственное пространство имен(Общие правила видимости и доступа не распространяются на них )
Пример:
struct S{
{
int a, b;//- у а область видимости struct S
} x;
int a;//-область видимости struct S
} s;
Ведено два типа: struct S и struct C.
int a; -глобальная переменная
struct S x;
x.a;- переменная из S
x.x.a;- переменная из С
Все имена тэгов собраны в единое пространство имен (неструктурированное), никакого отношения к обычным именам не имеют.
Пример:
struct time{...}// системное время
struct time time(...) - независимость пространства тегов и пространства имён ( название функции совпадает с именем структуры). Этим активно пользовались программисты на UNIX'ах.
Отличие:
struct C{}
В Си - только имя структуры struct C s;.
В Си++ struct C- имя типа C s; ( но если тег конфликтует с другим именем, то
struct C s;.)
В Си++ структура и класс отличаются только умолчанием прав доступа: в структурах по умолчанию public, а в классах - private.
Умолчание работает при:
-объявлении членов;
-наследование;
В остальном отличий нет!
В Java понятие структуры нет, его обобщает понятие класса.
В языке С# тоже есть понятие структуры, но оно уже очень сильно отличается от понятия класса. Структура - это урезанный класс.
Все типы Cи# делятся на:
1) значащие типы (типа значения; простые типы данных и структуры);
Выделение памяти- непосредственно при объявлении.
Если объект значащего блока внутри функции - выделение памяти в стеке. Если внутри класса - выделение памяти будет в куче.
Точно так же вместо объявления класса может стоять имя структуры. Увидев имя структуры, компилятор сразу будет выделять под неё память (а не создавать ссылку, как для классов).
Ограничения на структуры:
-Структура не может наследоваться и не может быть унаследована ни от чего (кроме типа Object).
-В структуре нельзя явно определять конструктор умолчания.( все объекты инициализируются неопределенным значением- любой доступ к ним ошибочен. NULL не является неопределенным значением!!! Автоматически генерируемый конструктор класса как раз и присваивает полям эти неопределённые значения.
Перекрывая конструктор умолчания, мы не гарантируем, что все поля будут инициализированы из соображений надежности и эффективности нельзя явно определять конструктор умолчания.)
2)референциальные (классы, массивы и интерфейсы)(располагаются в куче)
Момент размещения объекта в памяти выбирает сам программист при помощи операции new (за одним исключением - при инициализации массивов int []х={1,2,3} – new вызывается неявно) имя – ссылка.
Рразрешены только операции: точка и присваивание;
Опрация присвивания только копирует ссылки. И в таких ЯП должна быть операция для копирования объектов - операция типа Clone().
Пример:
Class T{
X a;
}
Если Х- класс, то а –ссылка на какой-то другой объект, который должен появиться с помощью оператора new.
Если Х- структура, то по объект а выделяется память:
Причины:
C# сгенерирован так, чтобы он претендовал на тотальный ЯП. Структуры в него введены только с целью эффективности. Разработчиками приводился пример, явно ссылающийся на язык Java для класса точки Point( некоторая структура с полями x,y и кучей методов, наследовать от Point не надо):
Point Java:
Point[] p = new Point[1000];- 1000 ссылок;
Инициализация: