лекции (2008) (Фингеров Александр_ Кононов Алексей_ Кузин Сергей), страница 4
Описание файла
Документ из архива "лекции (2008) (Фингеров Александр_ Кононов Алексей_ Кузин Сергей)", который расположен в категории "". Всё это находится в предмете "языки программирования" из 7 семестр, которые можно найти в файловом архиве МГУ им. Ломоносова. Не смотря на прямую связь этого архива с МГУ им. Ломоносова, его также можно найти и в других разделах. .
Онлайн просмотр документа "лекции (2008) (Фингеров Александр_ Кононов Алексей_ Кузин Сергей)"
Текст 4 страницы из документа "лекции (2008) (Фингеров Александр_ Кононов Алексей_ Кузин Сергей)"
type CHARS is (‘A’,’B’,’C’,’D’);
Считается, что любая программа была погружена в пакет STANDARD, где были описаны все специальные типы данных, которые покрывали английский язык. Если мы не использовали никаких преобразований в int, то нам вообще плевать в какой кодировке мы действуем. Позже в стандарте 95го года появился WIDECHARACTER, который кодировал Unicode, можно было свободно расширять Ada программы. Есть ещё и UCS-4, там 4 байта. Младшие 65536 символов совпадают с UCS-2 (это нужно китайцам, в самом Китае после реформы появился упрощенный китайский, но на Тайвани продолжал действовать традиционный). Вообще говоря, Unicode хороший формат, но приходится платить, потому что каналы коммуникации в отличие от памяти остались узким местом. До 90% информации из сети Интернет были на английском языке, в результате при переходе на Unicode половина информации будет составлять нули. Чтобы решить эту проблему, было решено, что наряду с Unicode и UCS были разработаны UTF, например, UTF-7 – преобразование кода в Юникоде в последовательность семибитных значений. Рабочие лошадки интернета 80х годов были семибитными, сейчас это уже линукс-сервера. UTF-8 - это способ представления в виде восьмибитных значений. Мы разбиваем весь диапазон на 0-127, 128-2047, 2048-65535. Английские тексты снова же одновременно являются и текстами в UTF-8. В третий диапазон попадают китайский, дальневосточный, японский и тд. На всех текстах из 2го и 3го диапазона мы имеем экономию. Если взять стандарт языка XML, то стандартная кодировка по умолчанию – UTF-8. Все современные ЯП, даже Дельфи, имеют представления в виде Unicode. Символьный тип данных есть во всех без исключения языках. Только в С и С++ имеется неявное их преобразование.
П.5. Порядковые типы
Диапазоны и перечисления.
Если есть базисный тип данных Т, то если L и R – константы типа Т, то диапазон это множество значений между L и R – L,..,R. Необходимы операции succ(v) и prev(v). Получения следующего и предыдущего. Важно, чтобы эта операция одинаково работала во всех реализациях. К каким типам нельзя применить получения следующего и предыдущего? Это плавающий тип. Не то чтобы не было такой операции – можно придумать, формально определить и для вещественного типа данных, но как только мы меняем систему – представление вещественного типа меняется, и succ и prev будут работать по-разному. Понятно, что только те типы, которые основаны изнутри на представление целого числа могут быть базовыми типами. Для перечислимых типов, например для (one, two, three), задаётся нумерация с нуля. Диапазоны были уже в языке Паскаль, а после Паскаля они появились почти в каждой языке – он есть и в языке Ада. {Type NEW_INT is new INTEGER range 1..N;} Диапазоны были и в Модуле 2, но там в понятие диапазона входили квадратные скобки. Диапазон удобен тем, что ограничивает поведение соответствующего типа, при использовании диапазонов можно использовать квазистатический контроль. При любом присваивании например x:=e, компилятор, если он может вычислить значение «е», то сразу проверяет, попадает ли оно в диапазон, либо вставляет код. Для диапазонного типа мы можем использовать меньше памяти, и он увеличивает надежность программы. Но интересен такой момент – цепочка языков, для которых он появился – везде есть диапазон. Следующий язык – Оберон (минимальность языковых конструкций). Если исключить что-то из Оберона, то язык становится почти недееспособен, но диапазонов там нет. В модификации языка Оберон – Оберон 2, где появилось понятие виртуальных методов, тоже нет диапазонов. В Java и C# тоже нет диапазонов. Проблему объяснил Клаус Вирт – основная идея Оберона – маленький и классический язык программирования, диапазон оказался лишним. Кроме минимальности – основное понятие языка Оберон, это понятие расширения типа, то есть в чистом виде наследование, как оно появилось в ОО языках. С точки зрения основной концепции расширения типа, диапазоны нам ничего не дают, более того, противоречат идее наследования - мы не можем расширить ТД диапазона. Диапазоны используются в понятии массива. Индекс образует некий диапазон. Понятие квазистатического контроля тоже тесно связано с понятием массива. Диапазон чаще всего использовался исключительно для работы с массивами, но все современные ЯП, как и Оберон, индексы размещают в диапазоне от 0 до N-1. {X:ARRAY N OF T} - где мы указываем только длину массива. Тип диапазон уже не соответствовал основной идее ОО программирования, и исчез. Примерно такая же история была и с перечислимыми ТД, но если диапазоны намертво выпали из ЯП, то перечисления вернулись в новые ЯП. Но о них мы поговорим на следующей лекции…
Лекция 9.
Пункт 5. Порядковые типы данных. Диапазоны, перечислимые типов данных.
С языка Паскаль популярность перечислимых типов данных стала возрастать.
{Type T = (c1, c2, … ,cn);} – такие части кода повышают читабельность программ. В перечислимом типе данных мы можем абстрагироваться от значений, таким образом, перечислимые типы данных повышают безопасность (нельзя присвоить отсутствующее значение в наборе) и читабельность программы. Тем не менее, тенденция была противоположной в 90-е годы:
-
Оберон (1988) – не было перечислимых типов, так как можно было писать системные программы без них и они противоречили концепции расширения типа (например, функция принимает перечислимый тип или возвращает, тогда при переопределении функции, она будет возвращать все равно ограниченный набор значений).
-
Java (1995) – не было перечислимых типов.
-
С# (1999) – был перечислимый тип (в документации на первой странице оправдываются, почему был включен перечислимый тип).
Таким образом, концепция перечислимых типов данных выдержала испытание временем и потому присутствует в современных языках программирования.
<Классическая реализация>
Паскаль, Ада, Модула-2 – классическая схема:
Type T = (c1,c2,…,cn); - Pascal, Mодула-2
Type T is (с1,c2,…,cn); - Ада
Нет неявных преобразований enum в int и наоборот! Но явные преобразования есть во всех языках. (Например, функция ord в Паскале переводит перечислимый тип в integer).
В Аде есть литералы перечисления – ‘A’. Таким образом, в Паскале, Модуле, Делфи – не могли пересекаться ни с каким другим именем, а в Аде это возможно. В Аде литералы перечислений могут пересекаться между собой. Например:
Type RGBColor is (… Red …);
Type TrafficColor is (Red, Green, Yellow);
------
Function Fun return T
объявление локальный переменных
Begin
операторы
End Fun;
X:T;
X:=Fun;
-------
Procedure P (x:RGBColor);
Procedure P(y:TrafficColor);
P(red); - неоднозначный вызов
Хотя верно – y:RGBColor; y := Red;
-------
Ада:
Указание типа
Тип ‘значение
Тип (значение)
Возможен вызов – P(RGBColor ‘Red);
------
В С# enum E {c1, c2, …}; -имена c1,c2… локализованы в типе E. Например:
Enum TextAlign {Right, Left, Center};
Text Align.Right; - только так можно обратиться к имени.
------
Перечислимый тип данных в Си – «недоносок, ублюдок какой-то».
enum C {c1 (=0), c2 (=1), c3 (=2)};
enum C x;
x = c1; - норм
x = -25; - норм
Это способ задания целочисленных констант. Также можно сделать и следующим способом:
Const int c1 = 0;
#define c1 0
В С++ возможно приведение enum->int - таким образом возможно расширение:
Enum E {c1,c2,c3};
E x;
X = E(25); - возможно
--
Enum Mode{ read = 0x1, Write = 0x2, ReadWrite = Read | Write}; – можем присвоить номер сразу. Таким образом, перечислимые типы данных гибкое средство. Недостаток – неявный импорт имен, например, возможно перемножение Write*Read… А обычно нужны только побитовые операции - &, |…
Рефлексия - отражение исходного кода в бинарный.
Аттрибутирование – приписывание некоторому типу данных атрибута. Например это есть в C#: можно написать перед enum [Flags] и можно будет использовать побитовые операции.
Недостаток в C# - нерасширяемость. В С# : Boxing/unboxing – упаковка/распаковка. В С# типы enum, struct, ПТД относятся к Value Types.
Все наследуется из Object:
В Java есть toString() – переводит в строку значение (для класса - его имя)
В C# соответственно ToString().
В Java:
Enum Color {Red, Green, Blue};
C = Color.Red;
Есть пакет (пространство имен) – java.lang – содержится описание классов, которые интегрируются компилятором. В остальном, enum – обычный класс.
Enum RGBColor {
Public RGBColor (byte R, byte G, byte B); – конструктор
Private int color;
Red (255,0,0);
Green(0,255,0);}
Таким образом, перечислимые типы данных превратились в классы. И единственное возражение – то что, они не являются абсолютно необходимыми.
Пункт 6. Ссылки, указатели.
-
Строгие языки (Паскаль, Ада, Модула-2, Оберон).
-
Нестрогие языки.
В Паскале они используются только для манипулирования объектами в динамической памяти. В стандартном языке Паскаль нельзя описать указатель на integer , указатели могут быть описаны только на структуры.
Ада:
Type PT is access T;
Модула-2, Оберон:
TYPE PT = POINTER TO T;
-----
В Аде:
TYPE PT is access;
Type T is record
Next:PT
End record
Type PT is access T;
------
X:PT;
X := new T;
Паскаль: NEW(X);
Операции: =, ^.
Оберон – X:PT
Разница между указателем и объектом исчезает.
Mодула-2 – X^.NEXT
Ада – X.NEXT, X.All – ссылка не весь объект целиком.
Лекция 10.
Указатели/ссылки.
Языки, в которых есть понятие указателя, делятся на 2 категории:
-
Строгие языки
-
Нестрогие.
В строгих - указатель служит исключительно для ссылки на анонимные объекты из памяти(New(P) – Оберон, Паскаль, x:=new T; - Ада, также еще Модула 2).
В нестрогих языках существует адресная операция. В С++ - &a, Delphi – @a (adr(a)).
char *f(){
char buffer[1024];
…
return buffer;
}
char *p=f();
*p=0;
В случае ошибки никто за руку не схватит.
static int i;
…
int *p;
p=&i;
free(p);
Существуют программы, которые могут найти такие ошибки – как например программа lint. Некоторые интегрированные среды поддерживают 2 режима - отладочный и продукционный. Первый работает существенно медленнее, так как есть отладочная библиотека памяти, но в состоянии отслеживать такие ошибки, причем такая ошибка могла случиться только в языках нестрогих. Есть ещё возможность неконтролированного преобразования: T*=>void*=>T1*<=>T*… В С void* это абстрактный адрес.
Большинство языков, которые мы изучаем, строгие. А решаются ли проблемы только строгостью языка? Ответ - нет. Операция явного освобождения памяти присутствует в Паскале, может присутствовать в Ада-83 и Модула-2. Явная операция освобождения памяти приводит к проблемам:
1. «мусор»
T* X=new T();
T* X1=new T();
X=X1;
Мусор это опасная проблема, когда программа работает в реальных условиях. Сложно построить последовательность действий пользователя, которая приведёт к утечке памяти. Как правило, ошибки в утечке памяти обнаруживаются в период эксплуатации, а при тесте они далеко не всегда обнаруживаются.
2. «висячие» ссылки
T* X=new X();
T* X1=X;
delete X;
Особенно неприятно, что некоторые менеджеры памяти спокойно на это реагируют. В системах с реальным параллелизмом вполне возможно, что другой поток захватит освобожденную память, и ошибка пролезет.
Системы с автоматической сборкой мусора. При каждом присваивании указателя - система автоматически увеличивает кол-во ссылок, при выходе из области видимости - счётчик уменьшается. Как только счётчик равен нулю, память можно утилизировать, тоесть она обозначается как пустая. Но его можно использовать только для достаточно высокоуровневых языков. В случае же кольцевых ссылок, мы запутаемся. Есть алгоритм, который запускается, смотрит на все указатели, и там где их нет, освобождает память. Однако в системах, где сильная нехватка памяти, эффективен он не будет. Нужно не просто быстро собирать память, а гарантированно быстро, поэтому в системах реального времени, где может начаться сборка мусора, любые автоматические сборщики малополезны. В языке Ада решили сделать по-другому. New – ключевое слово, а вот стандартных средств на уничтожение памяти в стандарте языка нет, поэтому создатели могут написать компилятор со своей сборкой мусора. Для большей переносимости существует стандартный пакет UNCHECKED_DEALLOCATION с соответствующей функцией DEALLOCATE, которая применима к любому указателю. Если компилятор не реализует автоматическую сборку мусора, то надо использовать её. В Модуле-2 есть проблемы, Вирт хотел сделать его в некотором смысле чистым языком СП, чтобы можно было на ней написать всю библиотеку языка, ОС и так далее. Он создал специальный модуль, который управляет распределением памяти, там есть 2 процедуры – ALLOCATE и DEALLOCATE, которые напоминают функции malloc и free. Для реализации этого, нам необходим специальный тип данных ADDRESS, иначе говоря, void*. Этот тип, разумеется, обладает свойством, что к нему может быть неявно приведён любой указатель. Чаще всего этот тип данных пролезал, когда программисты писали свои контейнеры. Единственный язык, который полностью сохранил концепцию и чистоту это язык Оберон. Там не существует понятия абстрактного адреса. С этой точки зрения Оберон самый надёжный язык. В C# и Java отсутствует понятие указателя, оно трансформируется в понятие ссылки (массивы, классы – всё суть ссылки). В С++ в случае X a; отводится память под объект. В С# и Java – под ссылки, а объекты анонимны и располагаются только в оперативной памяти, сам объект собственного имени не имеет. Как следствие a=new X(); a=b; не более чем копирование ссылок. Реализована и автоматическая сборка мусора.