Автор Анна Евкова
Преподаватель который помогает студентам и школьникам в учёбе.

Классификация языков программирования. Критерии выбора среды и языка разработки программ. ( Процедурные языки программирования )

Содержание:

Введение

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

Программирование берет свое начало в ХХ-ом веке, с появлением первых ЭВМ появилась необходимость каким-то образом взаимодействовать с машиной. Привычные многим языки программирования тогда ещё не были возможны к реализации, поэтому для «общения» инженера и вычислительной техники применяли простейшие методы – перфокарты и двоичные управляющие сигналы.

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

Целью работы является классификация языков программирования, а как частную задачу можно выделить рассмотрение типов переменных и данных в различных популярных языках программирования. Помимо этого стоит задача определения критериев выбора среды разработки и языка программирования для решения различных задач.

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

1. Классификация языков программирования

Все языки программирования можно разделить на несколько обширных категорий, в которые включатся определенные языки программирования на основе того, характерны или им те или иные свойства[1].

Рассмотрим одиин из вариантов классификации языков программирования – рисунок 1.

Рисунок 1 – классификация языков программирования.

В соответствии с рисунком в первую очередь языки программирования можно разделить на процедурные и непроцедурные.

2. Процедурные языки программирования

Процедурное программирование – программирование на языке, при котором происходит последовательное исполнение операторов, собранных в своеобразные коллекции – подпрограммы [2]. Оно отражает принципы архитектуры первых вычислительных устройств – архитектуры Фон Неймана.

В теории, примером процедурного программирования может служить вычислительная система, также известная под названием «Машина Тьюринга».

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

3. Машинно-зависимые языки программирования

Машинно-зависимые языки – это языки, наборы операторов и средства которых существенно зависят от особенностей ЭВМ (внутреннего языка, структуры памяти и т. д.). Эти языки называются языками программирования низкого уровня. Они ориентированы на конкретный тип процессора и учитывают его особенности. Операторы такого языка близки к машинному коду и ориентированы на конкретные команды процессора, то есть данный язык является машинно зависимым[3].

В качестве примера машинно-зависисмого языка можно привести язык Ассемблер. Это машинно-ориентированный язык программирования низкого уровня. Его команды прямо соответствуют отдельным командам машины или их последовательностям, также он может предоставлять дополнительные возможности облегчения программирования, такие как макрокоманды, выражения, средства обеспечения модульности программ.

Использование ассемблера позволяет максимально адаптировать программный код под конкретную платформу и обеспечить наибольшую производительность вычислений.

Ассемблер не имеет никаких альтернатив при создании драйверов устройств и ядер операционных систем, программ, которые будут храниться в постоянной памяти (BIOS, чипсеты и т.п.), платформо-зависимых компонентов и интерпритаторов языков программирования более высокого уровня, системных библиотек.

На самом деле, данный язык достаточно сложный и ввиду машинной ориентации программу на Ассемблере очень трудно прочитать. При программировании на данном языке усложняется процесс разработки, его стоимость, возникает повышенная вероятность возникновения ошибок. На рисунке 2 приведен пример кода на яызке Ассемблер.

Рисунок 2 – код программы на яызыке Ассемблер.

4. Машинно-независимые языки программирования

К машинно-независимым языкам программирования относятся те языки, операторы и языковые средства которых не зависят от конкретной платформы в физическом плане [4].

Среди машинно-независимых процедурных языков программирования стоит выделить несколько основных: BASIC, FORTRAN, Pascal. Рассмотрим Фортран – первый язык высокого уровня, который развивался достаточно долго.

Фортран— первый язык программирования высокого уровня, получивший практическое применение, имеющий транслятор и испытавший дальнейшее развитие. Создан в период с 1954 по 1957 год группой программистов под руководством Джона Бэкуса в корпорации IBM. Название Fortran является сокращением от FORmula TRANslator. Фортран широко используется в первую очередь для научных и инженерных вычислений. Одно из преимуществ современного Фортрана — большое количество написанных на нём программ и библиотек подпрограмм.

Fortran (Formula Translator) – исторически первый язык программирования, классифицируемый как язык программирования высокого уровня. Данный язык нашйл свою сферу применения и наплохо развивался в течение определенного времени. Фортран имеет транслятор и создавался небольшим коллективом разработчиков IBM с 1954 по 1957. Язык применяется в научных и инженерных вычислениях, на нём было написано большое количество подпрограмм и библиотек. На рисунке 3 приведена программа на языке Фортран.

Рисунок 3 – программа на яызке Фортран.

5. Непроцедурные языки программирования

Непроцедурный язык – это мощное средство разработки сложных программных систем, позволяющее сконцентрировать внимание разработчика на описании целей и правил, а не на последовательности действий по их реализации (т.е. описывается “что делать” вместо “как делать”). Непроцедурный язык ориентирован на конкретную предметную область (например, на автоматизацию технологических процессов на производстве). Различают два классических уровня реализации непроцедурного языка:

  • формализованное описание предметной области и логики функционирования системы без генерации исполнительного кода. Такой непроцедурный язык называется языком спецификаций. Он используется как средство формализации задачи и её постановки программистам.
  • полноценная управляющая система, построенная на непроцедурном языке. Сложности процедурной реализации перекладываются при этом на исполнительную систему, что обеспечивает ускорение разработки и значительное повышение надёжности создаваемых систем.

В свою очередь, непроцедурные языки подразделяются на объекто-ориентированные и декларативные [5].

6. Объектно-ориентированные языки программирования

Объектно-ориентированный язык программирования — язык, построенный на принципах объектно-ориентированного программирования.

В основе концепции объектно-ориентированного программирования лежит понятие объекта — некой сущности, которая объединяет в себе поля (данные) и методы (выполняемые объектом действия).

Основные принципы структурирования в случае ООП связаны с различными аспектами базового понимания предметной задачи, которое требуется для оптимального управления соответствующей моделью:

  • абстрагирование для выделения в моделируемом предмете важного для решения конкретной задачи по предмету, в конечном счёте — контекстное понимание предмета, формализуемое в виде класса;
  • инкапсуляция для быстрой и безопасной организации собственно иерархической управляемости: чтобы было достаточно простой команды «что делать», без одновременного уточнения как именно делать, так как это уже другой уровень управления;
  • наследование для быстрой и безопасной организации родственных понятий: чтобы было достаточно на каждом иерархическом шаге учитывать только изменения, не дублируя всё остальное, учтённое на предыдущих шагах;
  • полиморфизм для определения точки, в которой единое управление лучше распараллелить или наоборот — собрать воедино.

К объекто-ориентированным языкам относится большинство современных популярных языков программирования:

  • C#;
  • Java;
  • C++;
  • Visual Basic;
  • Delphi;

и многие другие языки.

В целом, объектный подход зарекомендовал себя как эффективное средство при разработке сложных программ и программных комплексов. Упрощение процесса разарботки достигается засчёт использования единожды написанных структур данных и методов, которые могут видоизменяться без повторного объявления, расширяться и обрабатывать различные данные.

Далее, при рассмотрении переменных и типов данных в языках программирования, будет сделан акцент именно на объектно-ориентированных языках, в частности C-подобных: C, C++, C#.

7. Декларативные языки программирования

Декларативное программирование — это парадигма программирования, в которой задаётся спецификация решения задачи, то есть описывается, что представляет собой проблема и ожидаемый результат.

К подвидам декларативного программирования также зачастую относят функциональное и логическое программирование — несмотря на то, что программы на таких языках нередко содержат алгоритмические составляющие, архитектура в императивном понимании (как нечто отдельное от кодирования) в них также отсутствует: схема программы является непосредственно частью исполняемого кода.

В качестве примера можно привести язык SQL. Это декларативный язык программирования, применяемый для создания, модификации и управления данными в реляционной базе данных, управляемой соответствующей системой управления базами данных.

Изначально SQL был основным способом работы пользователя с базой данных и позволял выполнять следующий набор операций:

  • создание в базе данных новой таблицы;
  • добавление в таблицу новых записей;
  • изменение записей;
  • удаление записей;
  • выборка записей из одной или нескольких таблиц (в соответствии с заданным условием);
  • изменение структур таблиц.

Со временем SQL усложнился — обогатился новыми конструкциями, обеспечил возможность описания и управления новыми хранимыми объектами (например, индексы, представления, триггеры и хранимые процедуры) — и стал приобретать черты, свойственные языкам программирования.

При всех своих изменениях SQL остаётся самым распространённым лингвистическим средством для взаимодействия прикладного программного обеспечения с базами данных. В то же время современные СУБД, а также информационные системы, использующие СУБД, предоставляют пользователю развитые средства визуального построения запросов. На рисунке 4 представлен пример кода на языке SQL.

Рисунок 4 – пример кода на языке SQL.

8. Переменные и типы данных в языках программирования

Любой язык программирования оперирует данными в процессе своего исполнения. Данные могут быть постоянными или изменяющимися, но вне зависимости от этого, их необходимо хранить. Для этого под данные отводятся адресованные пространства памяти. Именно благодаря механизму именования переменных, перед разработчиком не ставится задача прямой адресации области в памяти, в которой хранятся определенные данные. В этой задаче на помощь приходит переменная, которая как раз таки позволяет задать имя определенному пространству памяти. Приведем определение переменной.

Переменная — поименованная, либо адресуемая иным способом область памяти, адрес которой можно использовать для осуществления доступа к данным[6]. Переменные можно классифицировать по различным признакам, но в первую очередь – по типизации, т.е. к какому типу значений относится перменная.

9. Статическая типизация. Язык С#

Если тип данных определяется на этапе компиляции, имеет место статическая типизация, а если на этапе выполнения программы — динамическая. В последнем случае иногда говорят, что переменная не имеет типа, хотя данные, содержащиеся в ней, безусловно, относятся к определённому типу данных, но выясняется это уже во время выполнения программы.

В том случае, когда тип переменной задаётся при компиляции, имеет место статическая типизация. Это довольно широко используемый в программировании приём, который означает, что значение типа связывается с переменной в момент её объявления и не может быть изменён.

Статическая типизация имеет ряд преимуществ[7]:

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

Разумеется, существуют и некоторые недостатки, главным из которых является затрудненность работы с языками, которые имеют слабый математический фундамент: приходится каждый раз явно указывать, какой тип будет иметь переменная.

Рассмотрим далее один из статически типизированных языков программирования - C#.

C# - объектно-ориентированный язык программирования, который был разработан инженерами компании Microsoft в конце 1990-х годов и презентован в 2001-ом году. Первоначально C# задумывался как языковое средство разработки приложений для Microsoft .NET Framework[8].

Данный язык имеет C-подобный синтаксис и основан на статической типизации.

В C# существуют две категории типов: ссылочные типы и типы значений.

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

В C# типы значений подразделяются на несколько категорий. Рассмотрим каждую из них.

Простые типы. Простыми типами называют набор предопределенных типов структур, включающих ряд типов. Простые типы определяются при помощи ключевых слов, являющихся псевдонимами для предопределенных типов структур в пространстве имён System[9]. Главным отличием простых типов от структур является то, что простые типы допускают создание констант. Константы – это переменные, значения которых являются постоянными и не подразумевают изменения. Кроме создания констант, простые типы можно объявлять с помощью литералов, т.е. неявно. К примеру, запись ‘B’ будет являться переменной символьного типа, а запись 2003 - переменной целого типа. Типы переменных, относящихся к простым, представлены в таблице 1.

Таблица 1. Простые типы переменных в C#

Тип

Диапазон

Размер

sbyte

От -128 до 127

8-разрядное целое число со знаком

byte

От 0 до 255

8-разрядное целое число без знака

char

От U+0000 до U+ffff

Символ Юникода (16-разрядный)

short

От -32 768 до 32 767

16-разрядное целое число со знаком

ushort

От 0 до 65 535

16-разрядное целое число без знака

int

От -2 147 483 648 до 2 147 483 647

32-разрядное целое число со знаком

uint

От 0 до 4 294 967 295

32-разрядное целое число без знака

long

От -9 223 372 036 854 775 808 до 9 223 372 036 854 775 807

64-разрядное целое число со знаком

ulong

От 0 до 18 446 744 073 709 551 615

64-разрядное целое число без знака

float

От ±1,5 x 10−45 до ±3,4 x 1038

6–9 цифр

double

от ±5,0 × 10−324 до ±1,7 × 10308

15–17 цифр

decimal

от ±1,0 x 10-28 до ±7,9228 x 1028

28-29 знаков

bool

true, false

логический тип

Типы перечисления. Тип перечисления задается при помощи ключевого слова enum и по своей сути является списком констант, т.е. переменные типа перечислений могут иметь только одно из тех значений, которое входит в список, соответствующий данному именованному типу перечисления. В качестве примера можно привести пользовательский перечислимый тип Days:

enum Days{ ‘Понедельник’, ‘Вторник’, ‘Среда’, ’Четверг’, ’Пятница’, ’Суббота’, ’Воскресенье’}.

Таким образом, для переменной типа Days доступны только указанные значения дней недели. При компиляции все ссылки на отдельные значения преобразуются в числовые литералы.

Типы структур. Типы структур, как и классы, представляют способ создания собственных типов данных. По своей сути, структура схожа классу в ООП – может хранить состояние в виде переменных структуры и определять поведение при помощи методов структуры. Помимо этого, структуры имеют конструкторы, однако для создания переменной данной структуры, конструктор вызывать необязательно. Приведем пример структуры:

struct man

{

public string sex;

public string fullName;

public int age;

}

В примере продемонстрирована структура man, имеющая переменные пол, ФИО и возраст, которые в свою очередь являются переменными простых типов. Однако нет ограничения на применение структур и классов, перечислимых типов в качестве вложенных в структуру переменных.

Nullable. Данный тип переменных является расширением для любого типа и представляют все значения базового типа и null. Такой тип прекрасно подходит для ситуаций, когда необходимо представить переменную базового типа, значение которой ещё не определено.

Ссылочные типы.

К ссылочным типам относятся те типы данных, которые хранят в себе ссылку на объект. Одним из наиболее часто используемых ссылочных типов является тип класса.

К типу классов относятся следующие подтипы.

  • string;
  • object;
  • class.

Тип string представляет последовательность, состоящую из нуля или более символов в кодировке Юникод. Строки неизменяемы, т. е. содержимое созданного строкового объекта изменить нельзя, хотя синтаксис выглядит так, будто это возможно. Например, при написании кода компилятор фактически создает новый строковый объект для хранения новой последовательности символов, а затем этот новый объект назначается b.

Тип object является базовым для всех остальных типов и его методы полностью унаследованы любым из типов переменных в языке C#.

Тип class - это обертка (шаблон), которая определяет форму объектов. Объекты, в свою очередь, — это экземпляры класса. Определяя класс, мы создаем данные. В данном случае данные — это переменные-экземпляры класса. Над данными должен выполняться определенный код. Реализация кода находится в методах класса.

Interface. Тип интерфейса. Интерфейс содержит только сигнатуры методов, свойств, событий или индексаторов. Класс или структура, реализующие интерфейс, должны реализовать члены интерфейса, заданные в определении интерфейса.

Массив представляет собой совокупность переменных одного типа с общим для обращения к ним именем. Это хороший пример ссылочного типа переменных. Массивы могут быть n-мерными. Массивы служат самым разным целям, поскольку они предоставляют удобные средства для объединения связанных вместе переменных. Тем не менее, у них имеется одна особенность: они реализованы в виде объектов.

Delegate. Тип делегатов представляет собой основу для событий. Экземпляры делегата могут создаваться путем его связывания с именованным или анонимным методом. Делегат должен быть создан при помощи метода или лямбда-выражения, имеющего совместимые возвращаемый тип и входные параметры.

В данном разделе были рассмотрены типы переменных, реализованные путем статической типизации на примере языка C#. В языке C# переменные также подразделяются на две большие группы, включающие свои типы переменных: это типы значений и ссылочные типы.

10. Динамическая типизация в языках программирования и структуры данных

Под динамической типизацией понимается приём, когда тип переменной определяется её значением в момент их связывания, т.е. присвоения переменной значения. Ввиду этого, переменная может принимать значения различных типов в разные моменты выполнения программы[10].

Этот прием широко используется, чаще всего он применяется в языках программирования, которые ориентированы на веб и мобильные платформы: Objective-C, PHP, JavaScript.

Механизм динамической типизации обладает рядом достоинств:

  • упрощение написания простых программ и скриптов;
  • упрощается работа с СУБД.

Разумеется, динамическая типизация в некоторых случаях серьёзно проигрывает статической. В первую очередь, языки с динамической типизацией тратят большее количество вычислительных мощностей на динамическую проверку типа и хранение переменных. Кроме того, при использовании динамической типизации возникают ошибки, связанные с невнимательностью программиста: если в случае со статической типизацией, на этапе компиляции переменной целочисленного типа не удастся присвоить строковое значение, то в динамически типизируемых языках результат работы функции, где данные были перепутаны, будет известен только после выполнения кода.

На рисунке 5 представлен пример кода на JavaScript, где показан пример динамической типизации.

Рисунок 5 – пример динамической типизации в JavaScript.

Таким образом, переменная res может быть как строкой, так и числом.

Приведем обзор типизации в JavaScript. В этом языке любая переменная объявляется ключевым словом var, после которого следует название переменной. Это очень просто и удобно. Для объявления констант следует назвать переменную символами в верхнем регистре клавиатуры.

Стоит также рассмотреть некоторые динамические типы переменных. К ним относятся стек, куча и очередь.

Стек – тип данных, который по своей сути является списком элементов, организованных по принципу «последним пришёл – первым вышел».

Зачастую стек реализуется в виде однонаправленного списка (каждый элемент в списке содержит помимо хранимой информации в стеке указатель на следующий элемент стека).

Но также часто стек располагается в одномерном массиве с упорядоченными адресами. Такая организация стека удобна, если элемент информации занимает в памяти фиксированное количество слов, например, 1 слово. При этом отпадает необходимость хранения в элементе стека явного указателя на следующий элемент стека, что экономит память. При этом указатель стека (Stack Pointer, — SP) обычно является регистром процессора и указывает на адрес головы стека.

Предположим для примера, что голова стека расположена по меньшему адресу, следующие элементы располагаются по нарастающим адресам. При каждом вталкивании слова в стек, SP сначала уменьшается на 1 и затем по адресу из SP производится запись в память. При каждом извлечении слова из стека (выталкивании) сначала производится чтение по текущему адресу из SP и последующее увеличение содержимого SP на 1.

При организации стека в виде однонаправленного списка значением переменной стека является указатель на его вершину — адрес вершины. Если стек пуст, то значение указателя равно NULL.

На рисунке 6 представлен пример стека на языке С.

Рисунок 6. Стек на С. Пример реализации.

Возможны три операции со стеком: добавление элемента (иначе проталкивание, push), удаление элемента (pop) и чтение головного элемента (peek).

Во время операции push в стек добавляется новый элемент, который указывает на тот элемент, который до этого был головным. Сам элемент становится головным.

Операция удаления pop удаляет первый элемент стека, головным в свою очередь становится тот, на который указывал удаленный элемент. Значение убранного элемента возвращается.

Операция peek возвращает элемент, который в настоящее время является головным. Никаких модификаций стека не происходит.

На рисунке 7 приведен пример реализации операций со стеком на языке С.

Рисунок 7. Операции со стеком на С.

Стек в своем программном виде применяется при обходе структур данных: деревьев, графов. Аппаратный вид стека применяется при использовании рекурсивных функций. Кроме того, стек также используется при создании стековой машины, которая производит вычисления арифметических выражений в обратной польской записи.

Для отслеживания точек возврата из подпрограмм используется стек вызовов.

Арифметические сопроцессоры, программируемые микрокалькуляторы и язык Forth используют стековую модель вычислений.

Идея стека используется в стековой машине среди стековых языков программирования.

Куча (англ. heap) в информатике и программировании — название структуры данных, с помощью которой реализована динамически распределяемая память приложения[10].

Куча работает по следующему принципу. В момент запуска определенного процесса, операционная система отводит пространство адресов памяти для хранения данных кучи. В процессе работы размер этой памяти может быть изменен. Программный код при помощи специальных функций имеет возможность получить указатель на область памяти, в которой находится определенная часть кучи. Сама программа же использует кучу для хранения структур, которые создаются динамически. Также есть возможность освободить память, т.е. удалить данные из кучи при помощи специальных функций.

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

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

Функция, выполняющая получение указателя на занятую область памяти в куче, выполняет следующие действия:

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

Функция, освобождающая память кучи, выполняет следующие процедуры:

  • просмотр списка областей памяти, принадлежащих куче, поиск свободной (занятой) области необходимого размера;
  • удаление найденной области из списка занятых областей;
  • добавление очищенной области в список свободных.

Программа может быть уверена в том, что между вызовами функций, для поиска области или очистки, область памяти, помеченная как занятая, не будет выделена повторно. После вызова функции для освобождения памяти, область памяти будет освобождена и в дальнейшем может использоваться повторно или может быть отдана ОС. Использование указателя на освобождённую область памяти будет приводить к сбоям или непредсказуемой работе программы.

В алгоритмическом смысле куча представляет собой непрерывную область памяти, поделённую на занятые и свободные области (блоки) различного размера.

Информация о свободных и занятых областях кучи может храниться в списках различных форматов. От выбранного формата списка напрямую зависит производительность функций для работы с памятью, так как большую часть времени эти функции тратят на поиск по списку подходящих областей.

Для увеличения размера кучи функция использует системный вызов, при этом происходит переключение контекста из пространства пользователя в пространство ядра операционной системы и обратно. Поиск по списку занятых/свободных областей кучи выполняется быстрее, чем переключение контекстов, поэтому выгоднее один раз использовать системный вызов для выделения большой области памяти под кучу, а в дальнейшем выделять программе области меньшего размера из имеющейся крупной области с ведением списка занятых/свободных областей.

Количество элементов, входящих в список занятых/свободных областей кучи, может быть уменьшено путём слияния элементов, содержащих информацию о следующих друг за другом областях. Это позволит уменьшить время обхода списка.

Каждый элемент списка может хранить адрес области памяти, её размер, информацию о следующей (для поиска в прямом направлении) и/или предыдущей (для поиска в обратном направлении) области.

Очередью называют абстрактный тип данных, построенный по принципу «первый вошел – первый вышел», т.е. противоположность стеку. Добавить элемент можно только в конец очереди, и чем больше элементов добавлено, тем дольше придется ожидать обработки последнего. Очередь может быть реализована посредством применения динамических переменных.

Рассмотрим пример реализации очереди с помощью одномерного массива и двух переменных. Структурная схема изображена на рисунке 8.

Рисунок 8. Реализация очереди при помощи массива и двух переменных.

Переменные используются для определения начала и конца очереди. Назовем их start и end соответственно. Start определяет начало очереди, а end – элемент, который будет заполнен когда в очередь войдет новый элемент. В процессе добавления элемента в очередь, в q[end] помещается новый элемент, а значение end уменьшается на 1. В том случае, если значение end становится равным 0, то массив обходится циклически и значение переменной становится равным n.

Для извлечения элемента производится похожая последовательность действий, только вместо переменной end производится декремент переменной start. Такой алгоритм имеет изъян: одна из ячеек всегда будет пуста.

Пример реализации очереди в виде класса приведен на рисунке 9.

Рисунок 9. Класс «Очередь» на языке C#.

Вывод по разделу: переменной в языке программирования должен соответствовать какой-либо тип значения, хранящийся по адресу в памяти, на который она указывает. В зависимости от привязки переменной к типу, языки программирования разделяют на статически типизированные и динамически типизированные. Для первых характерна жёсткая привязка переменной к типу значения, которое она хранит, вторые же предоставляют возможность переменным хранить данные любого типа. Кроме того, существуют важные структуры данных, которые принято считать динамическими. Это стек, очередь и куча. Данные структуры находят широчайшее применение в том или ином виде при построении сложных программных конструкций, связанных с последовательной обработкой задач.

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

Под областью видимости в программировании понимается часть кода, в рамках которой переменная остается связанной с этой частью программы, т.е. позволяет обратиться к себе, совершить какие-либо операции с её использованием. Грубо говоря, переменная «видна» в какой-либо части кода – это и есть простое определение области видимости. По области видимости переменные разделяют на локальные и глобальные[11].

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

Благодаря использованию локальных переменных появляется возможность реализации рекурсивных процедур и функций. Применение локальных переменных также обусловлено принципом «разделяй и властвуй», что подразумевает использование своих переменных внутри сложных функций во избежание проблем, которые могут порождать глобальные переменные.

Среди локальных переменных следует выделить статические локальные переменные. Это специальная категория локальных переменных, которая реализована во многих популярных языках программирования. Особенность статической локальной переменной заключается в том, что она сохраняет свое значение в каждом вызове функции, и при каждом её повторном вызове будет иметь то значение, которое было ей присвоено по результатам предыдущего вызова.

В качестве примера для большего понимания локальных переменных представлен код на языке C (рисунок 10).

Рисунок 10. Локальные переменные в C. Пример.

Результатом выполнения программы выше будет последовательность ‘012’. Этот пример демонстрирует то, что глобальная переменная ‘a’ скрывается такой же переменной в локальных областях видимости.

Глобальными переменными называют такие переменные, для которых область видимости распространяется на всю программу, кроме тех функций, где она может быть намеренно скрыта. Как правило, глобальные переменные используют как инструмент взаимодействия между различными функциями или в качестве способа возврата значений из них.

Глобальные переменные имеют ряд недостатков при использовании. В первую очередь, глобальная переменная при определенных условиях может быть изменена в любой функции или методе. Это может повлиять на работу всей программы, поэтому глобальные переменные, используемые для создания взаимосвязей и зависимостей, вносят дополнительную сложность в код. С другой стороны, глобальная переменная может найти своё применение во избежание использования множества локальных переменных внутри отдельных функций, по своей сути отождествленных. В то же время, глобальные переменные делают процесс внедрения модулей более сложным, так как ранее спроектированный и используемый код может иметь такие же названия переменных, которые имеются во встраиваемом модуле.

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

На рисунке 11 представлен фрагмент кода на C, который можно привести в качестве примера использования глобальных переменных.

Рисунок 11. Глобальные переменные. Пример на С.

Вывод по разделу: классификация переменных на глобальные и локальные связана с необходимостью разделять их области видимости и ограничивать область существования локальных переменных. Локальные переменные используются в ограниченных секциях кода и не существуют за их пределами, глобальные – применяются для обмена данными между процедурами и функциями. Применение глобальных переменных может сопровождаться появлением блокировок в многопоточном коде.

12. Критерии выбора языка и среды разработки программного обеспечения

Для наиболее эффективного решения поставленных задач требуется грамотно и ответственно подойти к выбору языка программирования и среды разработки. При этом необходимо учитывать и такие особенности языка, как возможности представления данных и переменных, поскольку если речь идёт о программе, пердназначенной для интерпритации данных и передачи их между программами-потребителями данных, формат представления данных имеет решающее значение. Именно по этой причине переменные и типы данных были рассмотрены в предыдущей главе. На рисунке 12 представлена таблица приблизительного соответствия языка программирования решаемым с его помощью задачам.

Рисунок 12 – распределение языков программирования по решаемым задачам.

На начальном этапе создания программы так или иначе становится вопрос выбора языка программирования. Кто-то выбирает язык только из личных предпочтений, кто-то только потому, что знает только этот язык, кто-то об этом даже не задумывается. Однако, данный этап разработки является очень важным, так как от него в будущем могут возникнуть проблемы, а могут и не возникнуть — смотря как подойти к вопросу[12]. Определим основные критерии:

  • Скорость работы конечного продукта.

Требовательным к скорости выполнения могут быть программы с большим объемом математических вычислений, например моделирование физических систем, расчеты большого объема экономических данных, выведение трехмерной графики и прочее. Для данных целей хорошо подойдут компилируемые языки: ассемблер, С/С++, фортран и т.д. Почему именно такие? После сборки программа не требует (грубо говоря) ничего лишнего и содержит в себе машинные команды, которые выполняются без лишних задержек. Схема работы таких программ такая: 1) программа исполняется сразу, так сказать она самодостаточна и не требует дополнительных библиотек; 2) программа кроме своего кода содержит вызовы библиотек с машинным кодом (как системных, так и входящих в проект), поэтому, кроме исполнения собственно своих команд, программа вызывает функции из библиотек; 3) в дополнение случаям 1 и 2, программа может работать через прослойку драйверов, которые написаны на языках низкого уровня и работают по умолчанию быстро. Как видно, максимум в схеме возможны 4 блока: программа -> библиотеки -> драйвера -> железо.

  • Объем занимаемой оперативной памяти.

Данное требование появляется, когда программа разрабатывается для встраиваемых систем, мобильных платформ, микроконтроллеров и так далее. В данных случаях, чем меньше памяти расходует программа на данном языке – тем лучше. К таким языкам, опять же, относятся ассемблер, С/С++, Objective-C и другие. Список языков подобен списку пункта 1, так как чем меньше функциональных блоков в схеме исполнения, тем меньше занимается и памяти компьютера. Если данное требование некритично, то можно использовать «истинно высокоуровневые языки».

  • Скорость разработки программы.

Данное требование возникает тогда, когда начальник говорит «программа нужна не позже, чем вчера!» или еще какая срочность. Тогда выбор падает на высокоуровневые языки с максимально человеколюбивым синтаксисом. Это, например, Java, Flash и подобные. На данных языках время разработки может существенно сокращаться из-за обилия сторонних библиотек, максимально «очеловеченного» синтаксиса, и подобных вещей. Скорость выполнения программ, написанных на данных языках страдает, причем порой весьма ощутимо. Схема выполнения на примере Java:

Программа в виде байт-кода -> виртуальная машина-анализатор -> системные библиотеки -> драйвера -> железо.

Самым медленно работающим блоком в данной схеме является анализатор – он должен байт-код программы транслировать «на лету» в машинный код, при этом потратив много времени на точное определение инструкции. Поэтому быстрая разработка зачастую означает медленное выполнение.

  • Ориентированность на компьютер или человека

С кем будет работать программа в первую очередь? С человеком, или с компьютером? В пером случае программа должна обладать мощной графической частью, отвечающей требованиям дизайна и юзабилити. Разработка графической части зачастую требует достаточно много времени, т.к. отличается немалой сложностью. Здесь сложность возникает в том, что вывод графики – это немало математики, а значит присутствует требовательность к скорости исполнения, а из-за сложности разработки присутствует необходимость в высокоуровневом языке. В данном случае, на мой взгляд, очень хорошо подходит С++/C# с их одновременной и высокоуровневостью, и скоростью выполнения программ на них. Однако, если ГИП не очень сложный, но красивый, возможно использование Java и Flash, на которых создание красивых интерфейс гораздо проще, нежели на С++ и, тем более, С. Если программа ориентирована в первую очередь на «скрытую работу» с минимумом взаимодействия с пользователем, тогда выбор должен ложиться в сторону быстрых языков (ASM, C)

  • Кроссплатформенность.

Кроссплатформенность – возможность работы программы на различных платформах, в различных ОС с минимальными изменениями. В этой сфере можно выделить такие языки: Java, C#,Flash,C++ с различными библиотеками и другие, менее используемые, языки.

Java создавался с тем условием, что программы на данном языке должны работать на любой платформе, где есть JVM – Java Virtual Machine. Программы на Java вообще не требуют никаких изменений – после компиляции получается .jar файл, который будет работать и на Windows, и на Mac OS, и на Linux и еще немало где. Аналогичная ситуация и с Flash, только список платформ гораздо менее обширный. С С++ дело обстоит труднее. На чистом С++ написать кроссплатформенную программу довольно трудно, у кода возникает обширная избыточность, теряется достоинство в скорости выполнения. Облегчают задачу кроссплатформенные библиотеки, например, Qt, которые позволяют добиться принципа «один код на все платформы», однако на каждую платформу нужно программу собирать отдельно (при этом разными компиляторами).

В этот раздел так же можно включить интерпретируемые, скриптовые языки – для их работы нужно наличие интерпретатора языка в системе. Данные языки очень удобны в плане разработки, но достаточно медлительны. Схема их работы напоминает схему работы Java/Flash, только анализатор стал еще медленнее – полумашинный байт код анализировать «на лету» гораздо проще, чем человеческий код. Так же, это влечет к большему потреблению памяти.

  • Скорость внесения изменений, скорость тестирования

Проект стремительно развивается, в него постоянно вносятся изменения, порой немало? Тогда выбор должен падать на высокоуровневые языки, где любой функциональный блок можно быстро переписать. Для подтверждения – я думаю, гораздо проще дебажить тот же С++, чем ассемблер. А еще проще Java. Но тут очень много тонкостей, которые таятся даже не сколько в языке, сколько в разработчике с его стилем программирования и компиляторах. Тем не менее, язык вносит свою долю в это дело, так или иначе упрощая/осложняя работу программиста.

Заключение

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

В ходе проделанной работы была рассмотрена модель классификации языков программирования по различным критериям. Выснилось, что в своём большинстве языки программирования можно разделить на:

  • процедурные;
  • непроцедурные.

Среди данных двух категорий языки также разбиваются на несколько крупных классов:

  • машинно-зависимые;
  • машинно-независисмые;
  • декларативные;
  • объектно-ориентированные.

Были рассмотрены некоторые представители данных языков, в частности:

  • C#;
  • Java;
  • Assembler;
  • Fortran;
  • C;
  • JavaScript;
  • C++.

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

Не менее важным является и вопрос выбора языка программирования и среды разработки. Это по праву можно считать одним из определяющих моментов, который повлияет на жизненный цикл программного обеспечения, трудозатраты и надёжность, сложность разработки и финансовую составляющую. Была приведена сводная информация по соответствию различных языков программирования решаемым задачам и сформулированы основные критерии, которые определяют выбор языка программирования и среды разработки.

Список используемых источников

1.Казанский А.А. Объектно-ориентированное программирование на языке Microsoft Visual С# в среде разработки Microsoft Visual Studio 2008 и .NET Framework. 4.3 [Электронный ресурс]: учебное пособие и практикум/ Казанский А.А.— Электрон. текстовые данные.— М.: Московский государственный строительный университет, ЭБС АСВ, 2011.— 180 c.— Режим доступа: http://www.iprbookshop.ru/19258.html.— ЭБС «IPRbooks»

2.Васильев В.Н. Основы программирования на языке C+ [Электронный ресурс]: учебное пособие/ Васильев В.Н.— Электрон. текстовые данные.— Волгоград: Волгоградский институт бизнеса, Вузовское образование, 2010.— 72 c.— Режим доступа: http://www.iprbookshop.ru/11341.html.— ЭБС «IPRbooks»

3.Кирнос В.Н. Введение в вычислительную технику. Основы организации ЭВМ и программирование на Ассемблере [Электронный ресурс]: учебное пособие/ Кирнос В.Н.— Электрон. текстовые данные.— Томск: Томский государственный университет систем управления и радиоэлектроники, Эль Контент, 2011.— 172 c.— Режим доступа: http://www.iprbookshop.ru/13921.html.— ЭБС «IPRbooks»

4.Станевко Г.И. Информатика. Основы процедурного программирования на Паскале [Электронный ресурс]: учебное пособие/ Станевко Г.И., Колесникова Т.Г., Давыденко В.А.— Электрон. текстовые данные.— Кемерово: Кемеровский технологический институт пищевой промышленности, 2012.— 117 c.— Режим доступа: http://www.iprbookshop.ru/14366.html.— ЭБС «IPRbooks»

5.Устинов В.В. Основы алгоритмизации и программирования. Часть 1 [Электронный ресурс]: конспект лекций/ Устинов В.В.— Электрон. текстовые данные.— Новосибирск: Новосибирский государственный технический университет, 2010.— 40 c.— Режим доступа: http://www.iprbookshop.ru/44676.html.— ЭБС «IPRbooks»

6.Макаров А.В. Common Intermediate Language и системное программирование в Microsoft.NET [Электронный ресурс]/ Макаров А.В., Скоробогатов С.Ю., Чеповский А.М.— Электрон. текстовые данные.— М.: Интернет-Университет Информационных Технологий (ИНТУИТ), 2016.— 164 c.— Режим доступа: http://www.iprbookshop.ru/56316.html.— ЭБС «IPRbooks»

7.Зудилова Т.В. Web-программирование JavaScript [Электронный ресурс]/ Зудилова Т.В., Буркова М.Л.— Электрон. текстовые данные.— СПб.: Университет ИТМО, 2012.— 68 c.— Режим доступа: http://www.iprbookshop.ru/65749.html.— ЭБС «IPRbooks»

8.Туральчук К.А. Параллельное программирование с помощью языка C# [Электронный ресурс]/ Туральчук К.А.— Электрон. текстовые данные.— М.: Интернет-Университет Информационных Технологий (ИНТУИТ), Ай Пи Эр Медиа, 2019.— 189 c.— Режим доступа: http://www.iprbookshop.ru/79714.html.— ЭБС «IPRbooks»

9.Нечта И.В. Введение в информатику [Электронный ресурс]: учебно-методическое пособие/ Нечта И.В.— Электрон. текстовые данные.— Новосибирск: Сибирский государственный университет телекоммуникаций и информатики, 2016.— 31 c.— Режим доступа: http://www.iprbookshop.ru/55471.html.— ЭБС «IPRbooks»

10.Цветкова А.В. Информатика и информационные технологии [Электронный ресурс]: учебное пособие/ Цветкова А.В.— Электрон. текстовые данные.— Саратов: Научная книга, 2012.— 182 c.— Режим доступа: http://www.iprbookshop.ru/6276.html.— ЭБС «IPRbooks»

11. Балдин К.В. Математическое программирование [Электронный ресурс]: учебник/ Балдин К.В., Брызгалов Н.А., Рукосуев А.В.— Электрон. текстовые данные.— М.: Дашков и К, 2014.— 218 c.— Режим доступа: http://www.iprbookshop.ru/4558.html.— ЭБС «IPRbooks»

12. Смирнов А.А. Технологии программирования [Электронный ресурс]: учебное пособие/ Смирнов А.А., Хрипков Д.В.— Электрон. текстовые данные.— М.: Евразийский открытый институт, 2011.— 191 c.— Режим доступа: http://www.iprbookshop.ru/10900.html.— ЭБС «IPRbooks»