И.Г. Головин, И.А. Волкова - Языки и методы программирования, страница 7
Описание файла
PDF-файл из архива "И.Г. Головин, И.А. Волкова - Языки и методы программирования", который расположен в категории "". Всё это находится в предмете "языки программирования" из 7 семестр, которые можно найти в файловом архиве МГУ им. Ломоносова. Не смотря на прямую связь этого архива с МГУ им. Ломоносова, его также можно найти и в других разделах. .
Просмотр PDF-файла онлайн
Текст 7 страницы из PDF
Например,литеральные константы в C++ (целые, строковые и т.д.):int i = 12 8;ifstream inputCinput_file.txt") ;Однако понимать и (главное) модифицировать такие программы легче, если таким константам будут присвоены явные и ясныеимена:const BUFFER_SIZE = 128;const char * INPUT_FILE_NAME = "input_file.txt";В то же время существуют анонимные объекты данных, которыене могут иметь имени.
Например, это объекты, размещаемые в динамической памяти.Пример на языке C++:class X;X * рХ = new X ();29Указатель рХ ссылается на объект класса X, размещенный в динамической памяти. Этот объект никак не именуется и доступен толькочерез указатель на него.В языках Java и C# принята так называемая референциальнаямодель объекта, в которой все объекты классов размещаются исключительно в динамической памяти.
Имена есть только у объектовпростых типов данных и ссылок на объекты. Такой подход характерендля большинства объектно-ориентированных языков (примечательным исключением является язык C++).Глава 4ВИРТУАЛЬНАЯ МАШИНА ЯЗЫКА. ИЕРАРХИЯВИРТУАЛЬНЫХ МАШИНРассмотрим подробнее процесс выполнения на компьютерепрограмм, написанных на каком-либо языке программирования.Каждый компьютер обладает своей системой команд (машинных операций), которые обрабатывают данные, записанные в оперативнойпамяти. Данные представляются в двоичной форме (наиболее просто реализуемой с точки зрения аппаратуры). Машинные команды,как и обрабатываемые ими данные, также записываются в оперативную память, откуда последовательно выбираются для обработкицентральным процессором. Таким образом, машинные программытоже представлены в двоичной форме, и компьютер может выполнять только такие программы.
Непосредственно выполнить на такомкомпьютере программу, написанную на языке программированиятипа С или Лисп, нельзя.Для выполнения программы на каком-либо языке программирования существует специальная программа—транслятор. Трансляторсопоставляет программе на языке программирования эквивалентную(т.е.
осуществляющую эквивалентные вычисления) машинную программу. По способу реализации такого сопоставления трансляторыподразделяются на две категории: компиляторы и интерпретаторы.Обе категории трансляторов обрабатывают программу на языке программирования (назовем его L), но делают это по-разному.Компилятор читает программу на языке L и переводит ее в двоичную программу на машинном языке.
Далее эта программа непосредственно выполняется на компьютере.Интерпретатор читает программу на языке L и сразу же ее выполняет. Таким образом, интерпретатор не порождает эквивалентнуюпрограмму на машинном языке, а сразу выполняет эквивалентныевычисления. Подробнее процессы компиляции и интерпретации разбираются в ч.
III данного учебника. Заметим, что для любого языкаL можно написать как компилятор, так и интерпретатор.По разного рода причинам (часть из них обсуждается в ч. IIIданного учебника) время выполнения откомпилированной программы меньше, чем время на интерпретацию той же программы(в этом смысле говорят, что компиляторы «эффективнее», чем интерпретаторы). При этом разница во времени выполнения отком31пилированной и интерпретируемой программ на некоторых языкахвесьма существенная. Такие языки называют компилируемыми.Большинство индустриальных языков программирования относятсяк этому типу.Однако есть языки, для которых разница между компиляциейи интерпретацией не столь велика, а интерпретатор проще реализовать, чем компилятор. Такие языки называют интерпретируемыми.
Типичным примером интерпретируемого языка является Лисп.Вспомним, что программы на Лиспе обрабатывают списки. С другойстороны, программы на Лиспе представляются в виде списков. Неслучайно в Лиспе есть встроенная функция eval (сокращение отevaluate — вычислить значение), которая имеет один аргумент — список. Этот список рассматривается как Лисп-программа и вычисляется. По определению, eval — это интерпретатор языка Лисп. Можно,конечно, написать и компилятор языка Лисп (и такие реализациисуществуют), но интерпретатор Лиспа будет компактнее и удобнеекомпилятора.Какие же свойства языка влияют на компилируемость или интерпретируемость? Вспомним понятие связывания.
Чем больше статических связываний в языке программирования, тем более эффективнакомпиляция. Рассмотрим несколько связываний, которые сильновлияют на эффективность откомпилированных программ.Первый пример — связывание переменной с типом данных. Языки, в которых это связывание статическое, называются языками состатической типизацией (или статическими языками). Как правило, эти языки требуют опережающего объявления имен.
В такихязыках есть понятие объявления, связывающего переменную с ееатрибутами (в частности, с типом данных). Изменить тип переменнойнельзя. Значение переменной может относиться только к объявленному типу данных. Если тип значения, помещаемого в переменную(например, при выполнении оператора присваивания), отличен оттипа переменной, то либо транслятор выдает сообщение об ошибке,либо значение преобразуется к типу переменной (такая операцияпреобразования иногда называется приведением типа).Языки, в которых связывание переменной с типом динамическое,называют языками с динамической типизацией (или динамическими языками). Иногда в литературе встречается термин «бестиповыеязыки», но мы не будем его использовать, гак как даже в бестиповыхязыках типы данных есть (языков без типов данных просто не существует).
Правильнее говорить о бестиповых переменных в динамических языках. Такие переменные могут хранить значения любогодопустимого типа. Типичный пример динамически типизированногоязыка (как Лисп) — язык JavaScript. Переменные в нем объявлятьнеобязательно, но при этом существует понятие объявления переменной, например:32var x = 0,у = "string", z;В этом примере х, у, z связываются со своими именами. Приэтом две переменные также инициализируются значениями целого и строкового типа (третья получает неопределенное значение).Однако далее в эти переменные можно поместить любые другиезначения:X=у ;у=-1 ;Z=X,-Интересно, что в статически типизированном языке C# (начинаяс версии 3.0) существует синтаксически похожее понятие:var х = 0, у = "string";Однако смысл этого объявления сильно отличается от предыдущего примера.
Здесь переменные связываются не только с именами и инициализирующими значениями, но и с типами, которыеопределяются типами инициализирующих значений (очевидно, чтоинициализаторы в таких объявлениях обязательны, именно поэтомупеременная z из примера на JavaScript опущена в объявлении на С#).Говорят, что транслятор выводит тип переменной из инициализатора, поэтому это объявление эквивалентно следующему:int х = 0; string у = "string";Статическая типизация переменных обусловливает статичностьмногих других видов связывания, например связывание знака встроенной операции с ее смыслом.
Так, во фрагменте программы на языке C# первое вхождение операции «+» — это операция над целымичислами, а второе — операция сцепления двух строк:int а = 0, b = 1, с;с = а + Ь; //сложение целыхstring si = "STR", s2 = "ING", s3;si = s2 + s3; // сцепление строк,результат — STRINGТеперь рассмотрим фрагмент на языке JavaScript:var а, b, с;...
//какие-то операторы, рассмотрим их позжес = а+Ь;Какая конкретная операция соответствует здесь знаку «+» сказатьнельзя, так как связывание динамическое и происходит в моментвычисления выражения а+Ь. Определяется типами значений переменных а и Ь. Если вместо троеточия в примере стоят операторыа = 1; Ь = 2; то выполнится операция сложения целых, а еслиоператоры а = "STR" ; b = "ING"; то выполнится операциясцепления строк.33Очевидно, что выполнение программ в статических языках требуетменьше времени. Кроме того, ошибки в программах на статическихязыках легче обнаружить, так как типы операндов известны транслятору, поэтому некорректные операции, например сложение целогочисла и строки, обнаруживаются во время трансляции.Относительность процессов компиляции и интерпретации подчеркивает тот факт, что в реализации некоторых компьютерныхархитектур (в том числе и распространенной архитектуры х86) машинный язык сам является интерпретируемым.
Это связано с тем,что машинный язык в архитектуре х86 является довольно сложными включает в себя сотни машинных операций, множество режимовадресации и т.п.Непосредственная реализация процессоров этой архитектурывесьма сложна, поэтому инженеры-системотехники используют понятия микрокода и микроядра.
Микроядро можно рассматривать какспециализированный компьютер, являющийся частью более крупногоцентрального процессора. Микроядро обладает своей архитектурой,памятью и системой команд (микроопераций). Набор микроопераций достаточно невелик (существенно меньше, чем набор командархитектуры х86) и проще реализуется. Специализация микроядразаключается в интерпретации команд основного машинного языка.Упрощенно говоря, каждой команде процессора х86 соответствует набор микрокоманд (называемый микропрограммой, или микрокодом),который и интерпретирует (выполняет) соответствующую команду.Таким образом, микроядро является интерпретатором микрокода,реализованным аппаратно, в отличие от интерпретатора языка Лисп,реализованного программно. Подробнее машинные архитектуры(включая микропрограммные) рассмотрены в [10].Заметим, что пользователям компьютеров и даже прикладнымпрограммистам совершенно безразлично, как именно реализованамашинная архитектура: непосредственно или с помощью микроядра.Из всего сказанного вытекают два следствия.1.Если машинный язык можно интерпретировать с помощьюаппаратно реализованного интерпретатора, то это можно сделатьи с помощью программного интерпретатора.
Если мы имеем такую программу — интерпретатор машинного языка какой-либоархитектуры, то мы можем выполнять любые программы для этойархитектуры. Иначе говоря, мы имеем программно-реализованныйкомпьютер. Такой компьютер называют виртуальным (вообще в программировании виртуальным называют любую сущность, котораяполностью или частично реализована программно). Современныепроцессоры включают в себя команды, специально поддерживающиевиртуализацию (т.е. моделирование исполнения команд). Именноблагодаря такой программно-аппаратной реализации на современных компьютерах возможен одновременный запуск различных34вариантов операционных систем и прикладных программ. Правда,моделируемая машинная команда работает медленнее, чем команда,непосредственно выполняемая аппаратурой, поэтому программыпод управлением виртуальных машин работают «медленнее», чем на«родном» компьютере, однако в ряде случаев такими накладнымирасходами можно пренебречь.2.Язык программирования может интерпретироваться не тольпрограммно, но и аппаратно (по аналогии с машинным языком).Действительно, некоторые реализации трансляторов для языковпрограммирования были выполнены аппаратно (точнее, программноаппаратно).
Также были разработаны компьютеры с машинным языком, близким к языкам Алгол-60, Лисп, Ада и др. Правда, широкогораспространения эти машинные архитектуры не получили, посколькунаилучшего для всех проблемных областей языка не придумано(вспомним гл. 2), и постоянно появляются новые языки, в чем-топревосходящие уже существующие.