Учебное пособие по дисциплине Разработка языков программирования высокого уровня
Скачать 1.74 Mb.
|
3.3. Числа с фиксированной запятой.Большинство компьютеров, разработанных для коммерческих приложений, содержат аппаратное обеспечение, поддерживающее типы десятичных чисел fixed. Числа типа fixed представляются в форме с фиксированной точкой, которая стоит в установленном месте и определяет целую часть от дробной части (простые дроби). Такие типы данных в коммерческих приложениях и ЯП и КОБОЛ являются основными. К достоинствам следует отнести возможность осуществлять точную арифметику в установленном диапазоне. 3.4 Логический типВпервые логический тип данных появился в языках АЛГОЛ-60 и с 1960г. Данные этого типа были включены во все универсальные ЯПВУ, кроме C ( в языке C истинное значение кодируется любым числом, большим от нуля, ложное – 0). Три стандартные логические операции применяются к операндам логического типа, давая в результате логическое значение: логическое дополнение (not), логическое «И» (and), логическое «ИЛИ» (or). Основное назначение логического типа состоит в реализации условий для условного оператора и оператора цикла. Диапазон данного типа состоит из двух значений: true и false. Значения представляются в машине минимальной ячейкой памяти (байт). Основные операции: not – логическое дополнение, and – логическое «и», or – логическое «или». Используется в операторах условного перехода, операторах цикла, для организации в алгоритмах переключателей, флагов и т.п. 3.5. Символьный и строковый (string) типыСимвольный тип данных представляет собой один символ, внутреннее представление которого в компьютере реализуется одной из следующих систем кодирования: ASCII – код в диапазоне 0 – 127 занимает 7 бит, – код в диапазоне 0 – 255 занимает 8 бит (байт) Unicode – код символа реализуется двумя байтами, занимает 16 бит. Символьные строки – это последовательности символов. При этом реализация в разных зыках программирования различна. Так, в языках C, C++ строки вводятся как символьные массивы типа char. Набор операций над такими массивами вызывается из стандартной библиотеки string.h. Строки символов здесь завершаются специальным символом, который называется «нуль-байт» - /0. Этот нуль-байт заносится автоматически при создании строки символов. В языках Pascal (с версии 5.0), Delphi, Ada, Fortran77, 90, Бэйсик, Java тип string является встроенным. Существует несколько проектных решений, касающихся длины строковых величин: 1. Статическая реализация. Длина может быть статической и задаваться в объявлении: var str : string[10]; Строки со статической длиной всегда полные: если строковой переменной присвоили строку меньшей длины, то свободные места автоматически дополняются нулями. Дескриптор такой строки имеет следующий вид: 2. Строки переменной длины с ограничением. Текущая длина при этом ограничивается специальным символом, в C и C++ ставится нулевой байт. Дескриптор такой строки имеет следующий вид: 3. Строки с переменной неограниченной длинной (SNOBOL4, Perl). Такая реализация требует больших временных затрат на размещение и удаление из оперативной памяти, но обеспечивает высокую гибкость. Операции над строками делятся на два класса: 1. Встроенные: конкатенация (слияние строк), операции отношений и присваивания; 2. Реализованные в виде функций: выделение подстроки, определение длины строки, сравнение строк, определение индекса элемента и др. 3.6 УказателиУказатель – переменная, значением которой может быть адрес ячейки памяти или особый символ 0, т.е. нулевой адрес, который используется как пометка того, что указатель свободен. Указатели используются в следующих случаях: 1. Организация косвенной адресации для нединамических объектов. В частности, в языке C указатели широко используются при обращении к массивам, строкам, структурам. И это является одним из факторов ненадежности данного языка. 2.Для организации обращения к ячейкам динамической памяти (кучи). Переменные, размещенные в куче, не имеют имен, обращаться к ним можно только с помощью указателей и ссылок. Как любая переменная, указатель должен иметь тип. Тип указателя определяет объем динамической памяти в байтах, выделяемой из кучи. Однако в языках существует два вида указателей: 1.Типизированные, когда тип указателя определяется типом переменной, на которую он будет указывать. В нижеприведенном примере все объявленные указатели являются типизированными. Пример 1. 2. Нетипизированные, когда объем выделяемой памяти определяется с помощью специальных функций. Нетипизированные указатели существуют в языке Pascal, для их объявления задействован специальный тип pointer: Var p : pointer; Операции над указателями: 1. Инициализация указателя – присваивание указателю адреса. Может происходить в следующих случаях: – присваивание динамического адреса. По запросу пользовательской программы операционная система отводит блок ячеек динамической памяти и помещает адрес начальной ячейки в указатель, тем самым устанавливается связь между указателем и адресом. Для этого в языках используются специальные функции, например, Pascal – new(i); C – malloc(i); – обеспечение косвенной адресации для нединамического объекта, например, массива: int*(A+индекс)=*А+индекс*длина; – посредством операции взятия адреса (операция «амперсант»): i=ⅈ 2. Разыменование указателя – взятие (присваивание) значения из/в ячейки, на которую указывает указатель. Если, например, указатель i указывает на ячейку, которая содержит число 206, то оператор ii=*i; поместит это число в переменную ii, а оператор *i = 220; присвоит значение 220 в динамическую переменную, адрес которой содержится в указателе i. 3. Освобождение динамической памяти. Освободить память означает, что необходимо возвратить динамическую переменную в кучу, т.е. отдать ее адрес в распоряжение операционной системы. Для этого существуют специальные функции Dispose(i); (Pascal), Free(i); (C). Следует заметить, что, вообще говоря, эти функции не освобождают указатель, они только возвращают динамическую переменную в кучу. Для надежной работы программы необходимо производить и освобождение указателя (см. следующий пункт 4), хотя в некоторых языках операции 3 и 4 совмещены. 4. Освобождение указателя – помещение в него нулевого адреса (на примере языка Pascal). const c : pointer = NIL //нулевой адрес в Pascal … dispose(i); i = c; Только после того, как в указатель занесли 0, его можно использовать для другого адреса. 5. Присваивание. Операцию присваивания можно реализовывать только для однотипных указателей: var p1, p2 : ^int; p3 : ^real; pp : pointer; begin p1 = p2; p1 = p3; //ошибка,чтобы ее обойти, надо использовать // указатель pp: // pp = p3: // p1 = pp; end. 5. Арифметические операции над указателями (осуществляются в адресном пространстве): – операция «+»: int *p1; //длина int равно 4-м байтам p1 = 2000; p1 = p1+3; //оператор поместить в p1 адрес 2012 – операция «-»: int *p1, *p2, a; a = p2-p1; //смещение p2 относительно p1 6. Операция отношений: > , < , = , <= , >=. 7. Операция идентификация поля записи: p1 : ^record; //указатель на запись p1^ . fio; *p1.fio; Проблемы, возникающие при использовании указателей При использовании указателей возникают две основные проблемы: 1.Висячий указатель – указатель, содержащий адрес динамической переменной, которая уже удалена из доступной памяти и возвращена в кучу. Они создаются в следующей ситуации: Шаг 1: Пусть p1 указывает на некоторую ячейку; Шаг 2: p2 = p1; //присваиваем p2 значение p1 Шаг 3: возвращаем ячейку в кучу и освобождаем p1, но p2 при этом продолжает содержать адрес ячейки. Такая ситуация может привести к следующим последствиям: – в эту ячейку могут загрузиться новые данные или данные другой пользовательской программы, а, поскольку p2 имеет доступ к данной ячейке, то есть большая вероятность их испортить; – более фатальная ситуация может возникнуть, если эту ячейку (с доступом через p2) захватит операционная система. 2. Потерянные переменные – это ячейки, размещенные в динамической памяти и переставшие быть доступными для пользовательской программы. Такие переменные называются мусором. Они создаются в случае, если: Шаг 1: с целью указателя p1 устанавливается некоторая динамическая переменная, Шаг 2: целью того же p1 становится другая динамическая переменная. Тогда первая ячейка становится недоступной для программы, т.к. связь с ней теряется. Но она недоступна также и для операционной системы, т.к. тоже выделена программе. Эта проблема называется утечкой памяти и затрагивает ЯПВУ, в которых требуется явное удаление из памяти переменных. В некоторых языках организована автоматическая сборка такого мусора, который удаляет все эти ячейки, например, в языке Java. |