На что влияет количество памяти выделяемое для данных определенного типа

Основы C++(Лекции). Конспект лекций по курсу Программирование

2.2. Типы данных в Си++

wchar_t (расширенный символьный)

с плавающей точкой

(число=мантисса х 10 к )

double (вещественный с двойной точностью)
Существует 4 спецификатора типа, уточняющих внутреннее представление и диапазон стандартных типов

unsigned (беззнаковый)
2.2.1. Тип int

Значениями этого типа являются целые числа.

Размер типа int не определяется стандартом, а зависит от компьютера и компилятора. Для 16-разрядного процессора под него отводится 2 байта, для 32-разрядного – 4 байта.

Если перед int стоит спецификатор short, то под число отводится 2 байта, а если спецификатор long, то 4 байта. От количества отводимой под объект памяти зависит множество допустимых значений, которые может принимать объект:

long int – занимает 4 байта, следовательно, имеет диапазон –2 147 483 648..+2 147 483 647

Тип int совпадает с типом short int на 16-разрядных ПК и с типом long int на 32-разрядных ПК.

Модификаторы signed и unsigned также влияют на множество допустимых значений, которые может принимать объект:

unsigned long int – занимает 4 байта, следовательно, имеет диапазон 0..+4 294 967 295.

Значениями этого типа являются элементы конечного упорядоченного множества символов. Каждому символу ставится в соответствие число, которое называется кодом символа. Под величину символьного типа отводится 1 байт. Тип char может использоваться со спецификаторами signed и unsigned. В данных типа signed char можно хранить значения в диапазоне от –128 до 127. При использовании типа unsigned char значения могут находиться в диапазоне от 0 до 255. Для кодировки используется код ASCII(American Standard Code foe International Interchange). Символы с кодами от 0 до 31 относятся к служебным и имеют самостоятельное значение только в операторах ввода-вывода.

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

Предназначен для работы с набором символов, для кодировки которых недостаточно 1 байта, например Unicode. Размер этого типа, как правило, соответствует типу short. Строковые константы такого типа записываются с префиксом L: L“String #1”.

Тип bool называется логическим. Его величины могут принимать значения true и false. Внутренняя форма представления false – 0, любое другое значение интерпретируется как true.

2.2.5. Типы с плавающей точкой.

Внутреннее представление вещественного числа состоит из 2 частей: мантиссы и порядка. В IBM-совместимых ПК величины типа float занимают 4 байта, из которых один разряд отводится под знак мантиссы, 8 разрядов под порядок и 24 – под мантиссу.

Величины типы double занимают 8 байтов, под порядок и мантиссу отводятся 11 и 52 разряда соответственно. Длина мантиссы определяет точность числа, а длина порядка его диапазон.

Если перед именем типа double стоит спецификатор long, то под величину отводится байтов.

К основным типам также относится тип void Множество значений этого типа – пусто.

Общий вид оператора описания:

[класс памяти][const]тип имя [инициализатор];

Класс памяти может принимать значения: auto, extern, static, register. Класс памяти определяет время жизни и область видимости переменной. Если класс памяти не указан явно, то компилятор определяет его исходя из контекста объявления. Время жизни может быть постоянным – в течение выполнения программы или временным – в течение блока. Область видимости – часть текста программы, из которой допустим обычный доступ к переменной. Обычно область видимости совпадает с областью действия. Кроме того случая, когда во внутреннем блоке существует переменная с таким же именем.

Const – показывает, что эту переменную нельзя изменять (именованная константа).

При описании можно присвоить переменной начальное значение (инициализация).

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

extern – глобальная переменная, она находится в другом месте программы (в другом файле или долее по тексту). Используется для создания переменных, которые доступны во всех файлах программы.

static – статическая переменная, она существует только в пределах того файла, где определена переменная.

int a; //глобальная переменная

int b;//локальная переменная

extern int x;//переменная х определена в другом месте

static int c;//локальная статическая переменная

a=1;//присваивание глобальной переменной

int a;//локальная переменная а

a=2;//присваивание локальной переменной

::a=3;//присваивание глобальной переменной

int x=4;//определение и инициализация х

В примере переменная а определена вне всех блоков. Областью действия переменной а является вся программа, кроме тех строк, где используется локальная переменная а. Переменные b и с – локальные, область их видимости – блок. Время жизни различно: память под b выделяется при входе в блок (т. к. по умолчанию класс памяти auto), освобождается при выходе из него. Переменная с (static) существует, пока работает программа.

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

Имя переменной должно быть уникальным в своей области действия.

Унарные операции

&получение адреса операнда
*Обращение по адресу (разыменование)
унарный минус, меняет знак арифметического операнда

постфиксная операция увеличивает операнд после его использования.

постфиксная операция уменьшает операнд после его использования.

sizeofвычисление размера (в байтах) для объекта того типа, который имеет операнд

sizeof(1.0)//8, т. к. вещественные константы по умолчанию имеют тип double

Бинарные операции.

Аддитивные:

+бинарный плюс (сложение арифметических операндов)
бинарный минус (вычитание арифметических операндов)

Мультипликативные:

*умножение операндов арифметического типа
/деление операндов арифметического типа (если операнды целочисленные, то выполняется целочисленное деление)
%получение остатка от деления целочисленных операндов

Операции сдвига (определены только для целочисленных операндов).

Формат выражения с операцией сдвига:

операнд_левый операция_сдвига операнд_правый

>сдвиг вправо битового представления значения правого целочисленного операнда на количество разрядов, равное значению правого операнда, освободившиеся разряды обнуляются, если операнд беззнакового типа и заполняются знаковым разрядом, если – знакового

Поразрядные операции:

&поразрядная конъюнкция (И) битовых представлений значений целочисленных операндов (бит =1, если соответствующие биты обоих операндов=1)
|поразрядная дизъюнкция (ИЛИ) битовых представлений значений целочисленных операндов (бит =1, если соответствующий бит одного из операндов=1)
^поразрядное исключающее ИЛИ битовых представлений значений целочисленных операндов(бит =1, если соответствующий бит только одного из операндов=1)

Операции сравнения: результатом являются true( не 0) или false(0)

больше, чем
=больше или равно
==Равно
!=не равно

Логические бинарные операции:

&&конъюнкция (И) целочисленных операндов или отношений, целочисленный результат ложь(0) или истина(не 0)
||дизъюнкция (ИЛИ) целочисленных операндов или отношений, целочисленный результат ложь(0) или истина(не 0)

Операции присваивания

Формат операции простого присваивания:

Леводопустимое значение (L-значение) – выражение, которое адресует некоторый участок памяти, т. е. в него можно занести значение. Это название произошло от операции присваивания, т. к. именно левая часть операции присваивания определяет, в какую область памяти будет занесен результат операции. Переменная – это частный случай леводопустимого выражения.

В отличие от унарных и бинарных операций в ней используется три операнда.

Первым вычисляется значение выражения1. Если оно истинно, то вычисляется значение выражения2, которое становится результатом. Если при вычислении выражения1 получится 0, то в качестве результата берется значение выражения3.

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

int w=x%++y, если int x=1, y=2;

int a=++m+n++*sizeof(int); если int m=1, n=2;

float a=4*m/0.3*n; если float m=1.5; int n=5;

Источник

Размер Java объектов

Знаете сколько в памяти занимает строка? Каких только я не слышал ответов на этот вопрос, начиная от «не знаю» до «2 байта * количество символов в строке». А сколько тогда занимает пустая строка? А знаете сколько занимает объект класса Integer? А сколько будет занимать Ваш собственный объект класса с тремя Integer полями? Забавно, но ни один мой знакомый Java программист не смог ответить на эти вопросы… Да, большинству из нас это вообще не нужно и никто в реальных java проектах не будет об этом думать. Но это, ведь, как не знать объем двигателя машины на которой Вы ездите. Вы можете быть прекрасным водителем и даже не подозревать о том, что значат цифры 2.4 или 1.6 на вашей машине. Но я уверен, что найдется мало людей, которые не знакомы со значением этих цифр. Так почему же java программисты так мало знают об этой части своего инструмента?

Integer vs int

Все мы знаем, что в java — everything is an object. Кроме, пожалуй, примитивов и ссылок на сами объекты. Давайте рассмотрим две типичных ситуации:

В этих простых строках разница просто огромна, как для JVM так и для ООП. В первом случае, все что у нас есть — это 4-х байтная переменная, которая содержит значение из стека. Во втором случае у нас есть ссылочная переменная и сам объект, на который эта переменная ссылается. Следовательно, если в первом случае мы определено знаем, что занимаемый размер равен:

Забегая вперед скажу — во втором случае количество потребляемой памяти приблизительно в 5 раз больше и зависит от JVM. А теперь давайте разберемся, почему разница настолько огромна.

Из чего же состоит объект?
Структура заголовка объекта
Спецификация Java

Известно, что примитивные типы в Java имеют предопределенный размер, этого требует спецификация для переносимости кода. Поэтому не будем останавливаться на примитивах, так как все прекрасно описано по ссылке выше. А что же говорит спецификация для объектов? Ничего, кроме того, что у каждого объекта есть заголовок. Иными словами, размеры экземпляров Ваших классов могут отличатся от одной JVM к другой. Собственно, для простоты изложения я буду приводить примеры на 32-х разрядной Oracle HotSpot JVM. А теперь давайте разберем самые используемые классы Integer и String.

Integer и String

Итак, давайте попробуем подсчитать сколько же будет занимать объект класса Integer в нашей 32-х разрядной HotSpot JVM. Для этого нужно будет заглянуть в сам класс, нам интересны все поля, которые не объявлены как static. Из таких видим только одно — int value. Теперь исходя из информации выше получаем:

Теперь заглянем в класс строки:

И подсчитаем размер:

Ну и это еще не все… Так как строка содержит ссылку на массив символов, то, по сути, мы имеем дело с двумя разными объектами — объектом класса String и самим массивом, который хранит строку. Это, как бы, верно с точки зрения ООП, но если посмотреть на это со стороны памяти, то к полученному размеру нужно добавить и размер выделенного для символов массива. А это еще 12 байт на сам объект массива + 2 байта на каждый символ строки. Ну и, конечно же, не забываем добавлять выравнивание для кратности 8 байтам. Итого в конечном итоге простая, казалось бы, строка new String(«a») выливается в:

Важно отметить, что new String(«a») и new String(«aa») будут занимать одинаковое количество памяти. Это важно понимать. Типичный пример использования этого факта в свою пользу — поле hash в классе String. Если бы его не было, то объект строки так или иначе занимал бы 24 байта, за счет выравнивания. А так получается что для этих 4-х байтов нашлось очень достойное применение. Гениальное решение, не правда ли?

Размер ссылки

Немножко хотел бы оговорится о ссылочных переменных. В принципе, размер ссылки в JVM зависит от ее разрядности, подозреваю, что для оптимизации. Поэтому в 32-х разрядных JVM размер ссылки обычно 4 байта, а в 64-х разрядных — 8 байт. Хотя это условие и не обязательно.

Группировка полей
Зачем все это?

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

Источник

Разбираемся с управлением памятью в современных языках программирования

Привет, Хабр! Представляю вашему вниманию перевод статьи «Demystifying memory management in modern programming languages» за авторством Deepu K Sasidharan.

В данной серии статей мне бы хотелось развеять завесу мистики над управлением памятью в программном обеспечении (далее по тексту — ПО) и подробно рассмотреть возможности, предоставляемые современными языками программирования. Надеюсь, что мои статьи помогут читателю заглянуть под капот этих языков и узнать для себя нечто новое.

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

Часть 1: Введение в управление памятью

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

Для чего используется оперативная память?

Когда программа выполняется в операционный системе компьютера, она нуждается в доступе к оперативной памяти (RAM) для того, чтобы:

Стек используется для статичного выделения памяти. Он организован по принципу «последним пришёл — первым вышел» (LIFO). Можно представить стек как стопку книг — разрешено взаимодействовать только с самой верхней книгой: прочитать её или положить на неё новую.

На что влияет количество памяти выделяемое для данных определенного типа. Смотреть фото На что влияет количество памяти выделяемое для данных определенного типа. Смотреть картинку На что влияет количество памяти выделяемое для данных определенного типа. Картинка про На что влияет количество памяти выделяемое для данных определенного типа. Фото На что влияет количество памяти выделяемое для данных определенного типа
Использование стека в JavaScript. Объекты хранятся в куче и доступны по ссылкам, которые хранятся в стеке. Тут можно посмотреть в видеоформате

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

Почему эффективное управление памятью важно?

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

Различные подходы

Современные языки программирования стараются максимально упростить работу с памятью и снять с разработчиков часть головной боли. И хотя некоторые почтенные языки всё ещё требуют ручного управления, большинство всё же предоставляет более изящные автоматические подходы. Порой в языке используется сразу несколько подходов к управлению памятью, а иногда разработчику даже доступен выбор какой из вариантов будет эффективнее конкретно для его задач (хороший пример — C++). Перейдём к краткому обзору различных подходов.

Ручное управление памятью

Язык не предоставляет механизмов для автоматического управления памятью. Выделение и освобождение памяти для создаваемых объектов остаётся полностью на совести разработчика. Пример такого языка — C. Он предоставляет ряд методов (malloc, realloc, calloc и free) для управления памятью — разработчик должен использовать их для выделения и освобождения памяти в своей программе. Этот подход требует большой аккуратности и внимательности. Так же он является в особенности сложным для новичков.

Сборщик мусора

Сборка мусора — это процесс автоматического управления памятью в куче, который заключается в поиске неиспользующихся участков памяти, которые ранее были заняты под нужды программы. Это один из наиболее популярных вариантов механизма для управления памятью в современных языках программирования. Подпрограмма сборки мусора обычно запускается в заранее определённые интервалы времени и бывает, что её запуск совпадает с ресурсозатратными процессами, в результате чего происходит задержка в работе приложения. JVM (Java/Scala/Groovy/Kotlin), JavaScript, Python, C#, Golang, OCaml и Ruby — вот примеры популярных языков, в которых используется сборщик мусора.

Получение ресурса есть инициализация (RAII)

RAII — это программная идиома в ООП, смысл которой заключается в том, что выделяемая для объекта область памяти строго привязывается к его времени существования. Память выделяется в конструкторе и освобождается в деструкторе. Данный подход был впервые реализован в C++, а так же используется в Ada и Rust.

Автоматический подсчёт ссылок (ARC)

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

Автоматический подсчёт ссылок всё так же не позволяет обрабатывать циклические ссылки и требует от разработчика использования специальных ключевых слов для дополнительной обработки таких ситуаций. ARC является одной из особенностей транслятора Clang, поэтому присутствует в языках Objective-C и Swift. Так же автоматический подсчет ссылок доступен для использования в Rust и новых стандартах C++ при помощи умных указателей.

Владение

Это сочетание RAII с концепцией владения, когда каждое значение в памяти должно иметь только одну переменную-владельца. Когда владелец уходит из области выполнения, память сразу же освобождается. Можно сказать, что это примерно как подсчёт ссылок на этапе компиляции. Данный подход используется в Rust и при этом я не смог найти ни одного другого языка, который бы использовал подобный механизм.
На что влияет количество памяти выделяемое для данных определенного типа. Смотреть фото На что влияет количество памяти выделяемое для данных определенного типа. Смотреть картинку На что влияет количество памяти выделяемое для данных определенного типа. Картинка про На что влияет количество памяти выделяемое для данных определенного типа. Фото На что влияет количество памяти выделяемое для данных определенного типа

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

Читайте так же другие части серии:

Ссылки

Вы можете подписаться на автора статьи в Twitter и на LinkedIn.

Источник

Лабораторная работа № 5
Определения объема памяти для структур данных

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

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

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

Краткие теоретические сведения

Принципы распределения памяти

Распределение памяти XE «Распределение памяти» — это процесс, который ставит в соответствие лексическим единицам исходной программы адрес, размер и атрибуты области памяти, необходимой для этой лексической единицы. Область памяти XE «Область памяти» — это блок ячеек памяти, выделяемый для данных, каким-то образом объединенных логически. Логика таких объединений задается семантикой исходного языка.

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

Рис. 7. Область памяти в зависимости от ее роли и способа распределения

Классификация областей памяти представлена на рис. 7.

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

Виды переменных и областей памяти

Распределение памяти для переменных скалярных типов

Распределение памяти для сложных структур данных

Вот правила распределения памяти под основные виды структур данных:

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

Например, если рассмотреть фрагмент текста входной программы на языке Pascal :

arr1 = array[1..10,1..20] of integer;

case (v2: integer) of

arr2 = array[1..100] of rec2;

Выравнивание границ областей памяти

Архитектура многих современных вычислительных систем предусматривает, что обработка данных выполняется более эффективно, если адрес, по которому выбираются данные, кратен определенному числу байт (как правило, это 2, 4, 8 или 16 байт). Современные компиляторы учитывают особенности целевых вычислительных систем. При распределении данных они могут размещать области памяти под лексические единицы наиболее оптимальным образом. Поскольку не всегда размер памяти, отводимой под лексическую единицу, кратен указанному числу байт, то в общем объеме памяти, отводимой под результирующую программу, могут появляться неиспользуемые области.

Например, если мы имеем описание переменных на языке C:

static char c1, c2, c3;

то, зная, что под одну переменную типа char отводится 1 байт памяти, можем ожидать, что для описанных выше переменных потребуется всего 3 байта памяти. Однако если кратность адресов для доступа к памяти установлена 4 байта, то под эти переменные будет отведено в целом 12 байт памяти, из которых 9 не будут использоваться.

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

a1,a2: array [1..3] of char;

то при условии, что под одну переменную типа char отводится 1 байт памяти и кратность адресов установлена 4 байта, в случае, когда выравнивание границ областей памяти выполняется только по границам самих областей, под описанные выше переменные потребуется 8 байт памяти (по 4 байта на каждый массив). Если же выравнивание границ областей памяти выполняется и для составляющих элементов (в данном случае – элементов массивов), то под эти же переменные потребуется 24 байта памяти (по 4 байта на один элемент массива – и в целом 12 байт на каждый массив).

Как правило, разработчику исходной программы не нужно знать, каким образом компилятор распределяет XE «Распределение памяти» адреса под отводимые области памяти. Чаще всего компилятор сам выбирает оптимальный метод, и, изменяя границы выделенных областей, он всегда корректно осуществляет доступ к ним. Вопрос об этом может встать, если с данными программы, написанной на одном языке, работают программы, написанные на другом языке программирования (чаще всего, на языке ассемблера XE «Язык ассемблера» ), реже такие проблемы возникают при использовании двух различных компиляторов с одного и того же входного языка. Большинство компиляторов позволяют пользователю в этом случае самому указать, использовать или нет кратные адреса и какую границу кратности установить (если это вообще возможно с точки зрения архитектуры целевой вычислительной системы).

Размер типа arr 1 будет составлять V 1 = 10*20*4 = 800 байт. Размер типа rec 1 составит V 2 = 2+800 = 802 байта, но с учетом кратности (4 байта) для его размещения потребуется V 2‘ = 4+800 = 804 байта. Размер типа rec 2 V 3 = 1+4+ Max (1,2,4) = 9 байт с учетом кратности составит V 3‘ = 12 байт, а размер типа arr 2 с учетом кратности составит V 4‘ = 100*12 = 1200 байт. Тогда для размещения переменной aa 1 потребуется 800 байт памяти, для размещения переменных vv 1, vv 2, vv 3, vv 4 с учетом кратности потребуется 4*4 = 16 байт памяти, для переменной rr 1 – 804 байта, а для переменной aa 2 – еще 1200 байт. Таким образом, для размещения в памяти всей описанной структуры данных потребуется выделить V ‘ = 2820 байт памяти.

При тех же условиях, но с выравниванием границ памяти по элементам структур, необходимые объемы памяти в данном случае не изменятся. Размер типа arr 1 будет составлять V 1 = 10*20*4 = 800 байт. Размер типа rec 1 с учетом кратности составит V 2» = 4+800 = 804 байта. Размер типа rec 2 с учетом кратности составит V 3‘ = 4+4+ Max (4,4,4) = 12 байт, а размер типа arr 2 с учетом кратности составит V 4‘ = 100*12 = 1200 байт.

Видно, что объем требуемой памяти в обоих случаях увеличился примерно на 12.5%, но за счет этого увеличивается эффективность обращения к данным и, следовательно, скорость выполнения результирующей программы.

Виды областей памяти. Статическое и динамическое связывание

Глобальная и локальная память

Как правило, глобальная область памяти может быть доступна из любой части исходной программы, но многие языки программирования XE «Язык программирования» позволяют налагать синтаксические и семантические ограничения на доступность даже для глобальных областей памяти. При этом сами области памяти и связанные с ними лексические единицы XE «Лексема (лексическая единица)» остаются глобальными, ограничения налагаются только на возможность их использования в тексте исходной программы (и эти ограничения, в принципе, возможно обойти).

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

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

Рассмотрим для примера фрагмент текста модуля программы на языке Pascal XE «Язык программирования:Borland Pascal» :

Global_2 : integer = 2;

function Test (Param: integer): pointer;

Local_2 : integer = 2;

Статическая и динамическая память

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

Размер динамической области памяти будет известен только в процессе выполнения результирующей программы. Поэтому для динамической области памяти компилятор не может непосредственно выделить адрес — для нее он порождает фрагмент кода, который отвечает за распределение памяти (ее выделение и освобождение). Как правило, с динамическими областями памяти связаны многие операции с указателями и с экземплярами объектов (классов) в объектно-ориентированных языках программирования. При использовании динамических областей памяти XE «Связывание:динамическое» говорят о динамическом связывании области памяти и лексической единицы входного языка.

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

Как статические, так и динамические области памяти сами по себе могут быть глобальными или локальными.

Порядок выполнения работы

Требования к оформлению отчета

Отчет должен содержать следующие разделы:

· Задание по лабораторной работе.

· Краткое изложение цели работы.

· Запись заданной грамматики входного языка в форме Бэкуса-Наура.

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

· Пример выполнения разбора простейшей входной программы (по выбору).

· Текст программы (оформляется после выполнения программы на ЭВМ по согласованию с преподавателем).

Основные контрольные вопросы

1. Что такое распределение памяти? Какую роль оно играет в процессе компиляции?

2. Для каких элементов входной программы выполняется распределение памяти?

3. Что такое относительные адреса? В какой момент и как относительные адреса преобразуются в адреса физические?

4. Какие типы областей памяти бывают в зависимости от роли и способа распределения? Как они могут сочетаться между собой?

5. Что такое локальная и глобальная память? Как распределяется память для аргументов и локальных переменных процедур и функций?

6. Что такое динамическая и статическая память? Какие особенности можно указать для динамической памяти, распределяемой компилятором?

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

8. Что такое скалярные типы данных? От чего зависит объем памяти, выделяемой для скалярных типов данных?

9. Как рассчитывается объем памяти для сложных структур данных?

10. Что такое «кратность распределения памяти»? Для чего она используется?

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

12. Что такое дисплей памяти процедуры или функции? Расскажите о стековой организации дисплея памяти.

Варианты исходных грамматик

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

Входная грамматика описаний типов для программы описывается следующими правилами:

L ® T | L ;T L ® T | L ;T L ® T | L ;T

T ® t=c | t=D T ® t=c | t=D T ® t=c | t=D

R ® V | R ;V R ® V | R ;V R ® V | R ;V

V ® K :t | K :c | K :D V ® K :t | K :c | K :D V ® t:K | c:K | D :K

4. S ® type L ; var R ; 5. S ® type L ; var R ; 6. S ® type L ; var R ;

L ® T | L ;T L ® T | L ;T L ® T | L ;T

T ® t=c | t=D T ® t=c | t=D T ® t=c | t=D

R ® V | R ;V R ® V | R ;V R ® V | R ;V

V ® K :t | K :c | K :D V ® t:K | c:K | D :K V ® t:K | c:K | D :K

F ® E | F,E F ® E | F,E F ® E | F,E

Источник

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *