Студопедия

Главная страница Случайная страница

Разделы сайта

АвтомобилиАстрономияБиологияГеографияДом и садДругие языкиДругоеИнформатикаИсторияКультураЛитератураЛогикаМатематикаМедицинаМеталлургияМеханикаОбразованиеОхрана трудаПедагогикаПолитикаПравоПсихологияРелигияРиторикаСоциологияСпортСтроительствоТехнологияТуризмФизикаФилософияФинансыХимияЧерчениеЭкологияЭкономикаЭлектроника






Связывание. Сравнительная характеристика языков программирования






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

 

Cвязывание - процесс установления соответствия между объектами и их свойствами в программе на формальном языке (операции, операторы, данные) и элементами архитектуры компьютера (команды, адреса).


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

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

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

 

int a, b; … a+b …

 

1. Тип переменных int - как способ определения целой переменной в машинном слове стандартной длины (представление целого со знаком, дополнительный код), связывается с аналогичной формой представления данных в компьютере при определении языка. Язык Си характерен тем, что базовые типы данных в нем полностью совпадают с соответствующими формами представления данных в компьютере.

2. Конкретная размерность переменной int определяется при реализации соответствующего компилятора.

3. Имя a может быть определено в конструкции вида #define a 0x11FF. В этом случае имя (псевдо-переменная) связывается со своим значением на первой фазе трансляции - в препроцессоре.

4. Если переменная определяется обычным способом в виде int a; то связывание переменной с соответствующим ей типом происходит во время трансляции (на фазе семантического анализа).

5. Если переменная определяется как внешняя (глобальная, вне тела функции), то смысл ее трансляции заключается в распределении под нее памяти в сегменте данных программы, который создается для текущего модуля (файла). Но при этом сама распределенной памяти к конкретной оперативной памяти осуществляется в несколько этапов:

  • при трансляции переменная привязывается к некоторому относительному адресу в сегменте данных объектного модуля (то есть ее размещение фиксируется только относительно начала модуля)
  • при компоновке (связывании) сегменты данных и команд различных объектных модулей объединяются в общий программный файл, представляющий собой образ памяти программы. В нем переменная получает уже относительный адрес от начала всей программы.
  • при загрузке программы в некоторую область памяти (например, в DOS или в режиме эмуляции DOS в WINDOWS) она может размещаться не с самого начала этой области. В этом случае осуществляется привязка адресов переменных, заданных в относительных адресах от начала программного модуля к адресам памяти с учетом перемещения программного модуля (так называемый перемещающий загрузчик, которая имеет место для exe-файлов в DOS).
  • если программа работает не в физической, а в виртуальной памяти, то процесс загрузки может быть несколько иным. Программный модуль условно считается загруженным в некоторое виртуальное адресное пространство (с перемещением или без него как всей программы, так и отдельных ее сегментов). Реальная загрузка программы в память осуществляется уже в процессе работы программы по частям (сегментам, страницам), причем установление соответствия (или связывание) виртуальных и физических адресов осуществляется динамически операционной системой с использованием соответствующих аппаратных средств.

6. Если переменная определяется как автоматическая (локальная внутри тела функции или блока), то она размещается в стеке программы:

  • во время трансляции определяется ее размерность и генерируются команды, которые резервируют под нее память в стеке в момент входа в тело функции (блок). То есть в процессе трансляции переменная связывается только с относительным адресом в стеке программы;
  • связывание локальным переменной с ее адресом в сегменте стека осуществляется при выполнении в момент входа в тело функции (блок). Благодаря такому способу связывания в рекурсивной функции существует столько «экземпляров» локальных переменных, сколько раз функция вызывает сама себя.

7. Тип операции “+” в конкретном выражении a+b определяется при трансляции в зависимости от типов операндов. В данном случае генерируется операция целого сложения.

8. С точки зрения времени связывания понятие инициализация внешних переменных можно определить как связывание переменных с их значениями в процессе трансляции программы (int a=10;) С этой точки зрения обычное присваивание можно рассматривать как связывание переменной с ее значением во время выполнения программы.

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

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

 

int A[100];

 

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

 

double *p;

p = malloc(sizeof(double)*n); // или

p = new double[n];

for (i=0; i< n; i++) p[i] = 5.44;

// ПРИМЕР СИНТАКСИСА ДИНАМИЧЕСКОГО МАССИВА

// n = getnum();

// ReDim double A[n];

 

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

double *p; int n=10;

p = malloc(sizeof(double)*n);

for (i=0; i< 10000; i++)

{

if (i > = n) // Если надо,

{ n = n+10; // увеличить размерность

p = realloc(p, sizeof(double)*n);

}

p[i] = 5.44

}

// ПРИМЕР СИНТАКСИСА ВИРТУАЛЬНОГО МАССИВА

// ReDim double A[];

// for (i=0; i< 10000; i++) A[i]=5.44;

 

  • виртуальный массив, в котором может меняться не только размерность, но и типы хранящихся в нем элементов, можно смоделировать в Си с помощью динамического массива указателей на переменные - элементы массива:

 

void **p; int n=10;

p = malloc(sizeof(void*)*n);






© 2023 :: MyLektsii.ru :: Мои Лекции
Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав.
Копирование текстов разрешено только с указанием индексируемой ссылки на источник.