Skip to content

Latest commit

 

History

History

C++

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 

Управление памятью в С/С++

  • Вид работы: курсовой проект (работа)
  • Дисциплина: динамические средства разработки программного обеспечения
  • Время создания работы: апрель-май 2019

ВВЕДЕНИЕ

Курсовая работа посвящена тому, как происходит процесс управления памятью в языках программирования С и С++.

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

Современные операционные системы (ОС) полностью обеспечивают управление ресурсами ЭВМ, поэтому прикладным программам физическая память напрямую недоступна. В ядре ОС имеются средства, обеспечивающие единообразную работу с любой физической памятью, — часть слоя абстракции от оборудования (Hardware Abstraction Layer, HAL). Для прикладного программиста он недоступен.

Прикладные программы могут взаимодействовать с механизмами ОС по работе с памятью посредством интерфейса прикладных программ ОС (Application Programming Interface, API). Он представляет собой набор функций в библиотеках, написанных программистами-разработчиками ОС, и документирован.

С++ - один из наиболее популярных языков программирования. Он используется для разработки программного обеспечения, поддерживает, объектно-ориентированное, обобщенное программирование, сочетает свойства высокоуровневых и низкоуровневых языков. С++ используется для создания программ, рассчитанных на выполнение определенных задач в разных сферах жизни человека, игр, драйверов, операционных систем и прочего. Он образован на основе языка С. Многие программы на С исправно работают и с компилятором С++ из-за тесной связи. Во многих языках программирования, в отличие от С/С++, нам не нужно заботиться об управлении памятью (так как за вас это делает движок языка), но это не делает проблему управления памятью менее важной. Знания возможностей и ограничений менеджера памяти критически важно для эффективного программирования. Программируя на С и С++, мы должны самостоятельно заботиться об управлении памятью, а значит, нужно знать, как организована память и какие приёмы существуют для работы с ней.

В курсовой работе я постараюсь описать базовые принципы и приёмы работы с памятью в С и С++ и привести примеры использования элементов управления памятью. В первой главе будут рассмотрены основные принципы работы с памятью, а во второй главе – приёмы и средства управления памятью в языках C и C++.

1. Основные принципы работы с памятью в С/С++

1.1. Особенности языка С при использовании памяти

Язык С++ унаследовал практически все черты языка С, а тот, в свою очередь, разрабатывался как высокоуровневая (независимая от платформы) замена ассемблеру, в котором все обращения к памяти ЭВМ производились явно. Принцип «презумпции ненужности» препятствовал появлению сугубо языковых (недоступных для модификации программисту) средств работы с памятью. С другой стороны, тот факт, что стандарт С++ описывает выполнение программы на абстрактной машине, побудил создателей языка разработать гибкую структуру организации памяти с точки зрения С++, которая как не была бы привязана к аппаратной платформе, так и оставляла бы разработчикам компиляторов и программ максимум свободы выбора реализации. Из всего названного следуют характерные черты подхода С++ к использованию памяти:

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

1.2. Организация памяти

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

Упорядоченность означает, что каждый элемент последовательности (каждая ячейка памяти) имеет свой порядковый номер. Этот порядковый номер называют адресом ячейки памяти — адресом байта, который принято записывать как шестнадцатеричное число с префиксом «0x». Непрерывный диапазон ячеек, доступный для адресации в конкретной операционной системе, называют адресным пространством.

Независимо от физического исполнения ОЗУ ЭВМ и от организации памяти, предлагаемой ОС, с точки зрения языка С++ память ЭВМ всегда описывается одинаково. Это описание называется моделью памяти С++. Наименьшая единица памяти в С++ — байт. Память, доступная программе на С++, состоит из последовательностей байт (ненулевой длины), называемых областями памяти (memory location).

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

1.3. Распределение памяти

Скомпилированная программа C имеет четыре логически обособленные области памяти. Первая – это область памяти, содержащая выполнимый код программы. Во второй области хранятся глобальные переменные.

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

Оставшиеся две области – это стек и динамически распределяемая область памяти. Стек — это область оперативной памяти, которая создаётся для каждого потока. Он работает в порядке LIFO (Last In, First Out), то есть последний добавленный в стек кусок памяти будет первым в очереди на вывод из стека. Каждый раз, когда функция объявляет новую переменную, она добавляется в стек, а когда эта переменная пропадает из области видимости (например, когда функция заканчивается), она автоматически удаляется из стека. Когда стековая переменная освобождается, эта область памяти становится доступной для других стековых переменных.

Из-за такой природы стека управление памятью оказывается весьма логичным и простым для выполнения на ЦП; это приводит к высокой скорости, в особенности потому, что время цикла обновления байта стека очень мало, т.е. этот байт скорее всего привязан к кэшу процессора. Тем не менее, у такой строгой формы управления есть и недостатки. Количество памяти стека ограничено компилятором и, как правило, значительно меньше размера кучи (на платформе x86 для компилятора Microsoft Visual Studio C++ размер стека по умолчанию равен 1 МБ).

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

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

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

Стек используется для хранения вспомогательных переменных во время выполнения программы. Здесь находятся адреса возврата функций, аргументы функций, локальные переменные и т. п. Текущее состояние процессора также хранится в стеке.

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

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

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

В сравнении со стеком, куча работает медленнее, поскольку переменные разбросаны по памяти, а не сидят на верхушке стека. Некорректное управление памятью в куче приводит к замедлению её работы; тем не менее, это не уменьшает её важности — если вам нужно работать с динамическими переменными, пользуйтесь кучей.

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

Рис. 1.1 - Распределение памяти (карта памяти) при выполнении программ, написанных на языке C.

1.4. Время жизни данных и способы их хранения

Время жизни объекта— это период во время выполнения программы, когда данные находятся в памяти. Любые свойства данных справедливы только в этот период. Время жизни простых типов данных начинается с момента, когда для них (компилятором или программистом) выделена область памяти, а заканчивается с освобождением (явным или неявным) этой области. Минимальное время жизни данных называется временем хранения, оно зафиксировано в стандарте С++ и всегда известно.

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

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

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

2. Приёмы и средства управления памятью в С/С++

2.1. Указатели

Указатель – это переменная, значением которой является адрес некоторого объекта в памяти компьютера. Основные функции указателей: • средство, с помощью которого функция может изменять значения передаваемых в неё переменных; • выполнение динамического распределения памяти; • повышение эффективности процедур. Указатель ссылается на самое начало блока данных в области памяти. Если мы знаем адрес переменных или функций, то мы можем создать на них указатель. Объявление указателя состоит из имени базового типа, символа * и имени переменной (см. листинг 2.1).

int *pointer1; //объявление указателя на целое число
char *pointer2; //объявление указателя на символ
void *pointer3; //объявление указателя на неопределённый тип

Листинг 2.1 – Объявление указателей.

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

void *ptr1;
int *ptr2;
ptr1=ptr2; //неявное преобразование указателя типа int*
ptr2=(int)ptr1; //явное преобразование указателя типа void*
//ptr2=ptr1 // неявное преобразование указателя типа void* недопустимо

Листинг 2.2 – Использование операции приведения типов над указателями.

Разрешено также преобразование целого в указатель и наоборот. При этом используется операция приведения типов (см. листинг 2.2).

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

int *ptr=0; //1-ый вариант
int *ptr(NULL); //2-ой вариант
int *ptr = nullptr; //3-ий вариант

Листинг 2.3 – Примеры инициализации нулевого указателя.

В языке С существует две основные операции для работы с указателями: операция взятия адреса (&) и операция разыменования (возврат значения переменной, расположенной по указанному адресу) (*).

Над указателями можно выполнять некоторые арифметические операции, такие как суммирование и вычитание. Предположим, что указатель ptr ссылается на целочисленную переменную, размещённую по адресу 1000. Кроме того, будем считать, что целые числа занимают 4 байта. Тогда после вычисления выражения ptr=ptr+1 переменная ptr будет равна не 1001, а 1004, поскольку после увеличения указателя на 1, он ссылается на область памяти, которая находится за целочисленной переменной, на которую указатель ссылался ранее. При использовании арифметических операций над указателями следует помнить, что адресная арифметика тесно связана с базовым типом.

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

int a=5;
int *pa=&a; //инициализация указателя
cout << *pa << endl; //вывод значения по адресу, на который ссылается указатель  
                     //вывод: 5                  
cout << &pa << endl; //вывод адреса указателя
pa++; //увеличение значения адреса, на который ссылается указатель, на 4
      //т. к. pa – указатель на переменную типа int, которая занимает 4 байта
pa--; // уменьшение значения адреса, на который ссылается указатель, на 4
int *pa2=&a;
if (pa==pa2) cout <<  "equally" << endl; //вывод: equally, т. к. указатели ссылаются
                                         //на один и тот же адрес

Листинг 2.4 – Пример операций над указателями.

Указатели можно собрать в массив, как и объекты других типов. Также можно создавать указатель на указатель. Это называют многоуровневой или косвенной адресацией (см. листинг 2.5).

int a[10], *pa, x;
pa=a; //инициализация указателя на первый элемент массива a
x=*(pa+6); //присвоение переменной x значения 7-ого элемента массива a
int *ap[10]; // объявление массива из 10 указателей на целое число
int **ppa=&pa; //создание указателя на указатель

Листинг 2.5 – Пример указателя на массив, массива указателей и косвенной адресации.

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

const int a;
const int *pa=&a; //инициализация указателя на константу
int b;
int *const pb=&b; //инициализация константного указателя
const int c;
const int *const pc=&pc; //инициализация константного указателя на константу

Листинг 2.6 – Инициализация константного указателя и указателя на константу.

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

В языке C/C++ на структуры тоже можно ссылаться. Указатели на структуры используются в двух ситуациях: для передачи структуры в функцию по ссылке и для создания структур данных, основанных на динамическом распределении памяти (например, связанных списков).

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

Указатели могут ссылаться на объекты. Для доступа к членам класса через указатель на объект используется оператор “->”, а не “.”. При увеличении указателя на единицу он перемещается на следующий элемент того же типа. Например, целочисленный указатель будет ссылаться на следующее целое число. Как правило, адресная арифметика зависит от типа указателя. Это правило касается и указателей на объекты. Указателю можно присвоить адрес открытого члена объекта, а затем ссылаться на этот член с его помощью.

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

2.2. Ссылки

В языке C++ есть переменные, очень напоминающие указатели. Они называются ссылками. Ссылка — это переменная, связанная с другой переменной таким образом, что обращение к ссылке равносильно обращению к переменной.

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

Ссылки на неконстантные значения могут быть инициализированы только неконстантными переменными. Они не могут быть инициализированы с помощью констант, иначе мы могли бы изменить значение константного объекта через ссылку, что уже является нарушением понятия «константа», или с помощью временного значения без определенного адреса памяти и с областью видимости выражения (см. листинг 2.7).

int a;
int &ra=a; //инициализация ссылки
//int &rb; некорректно создавать ссылку, которая никуда не ссылается
const int c;
//int &rc=c; нельзя создать неконстантную ссылку на константу
//int &rd=8; нельзя создать неконстантную ссылку на временное значение, не
             //имеющее адрес
const int &ra2=a; //инициализация константной ссылки на неконстантную переменную
const int &rc2=c; //инициализация константной ссылки на константу
const int &rd2=8; //инициализация константной ссылки на временное значение без
                  //адреса (увеличиваем с помощью ссылки область видимости этого
                  //временного значения)

Листинг 2.7 – Инициализация константных и неконстантных ссылок.

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

  • Не существует «нулевой ссылки», подобной нулевому указателю.
  • Не существует ссылок на ссылки, указателей на ссылки и массивов ссылок.
  • Не бывает переменных типа void, поэтому не бывает и ссылок на void.
  • Разыменование ссылки не имеет смысла (ссылка — не адрес, а псевдоним).
  • У ссылки может не быть собственного адреса, поэтому операция взятия адреса ссылки срабатывает для переменной, на которую ссылаются.
  • Ссылки отсутствуют в языке С.

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

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

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

Как известно, память для глобальных переменных выделяется на этапе компиляции. Локальные переменные хранятся в стеке. Следовательно, в ходе выполнения программы невозможно объявить новые глобальные или локальные переменные. И все же иногда в ходе выполнения программы возникает необходимость выделить дополнительную память, размер которой заранее неизвестен. Например, программа может использовать динамические структуры — связанные списки или деревья. Такие структуры данных являются динамическими по своей природе. Они увеличиваются или уменьшаются по мере необходимости. Для того чтобы реализовать такие структуры данных, программа должна иметь возможность распределять и освобождать память. Для работы с динамической памятью в языке С используются следующие функции: malloc, calloc, free, realloc. Для выделения памяти используется функция malloc, которая выделяет заданное количество байтов памяти и возвращает указатель на этот блок памяти. Функция calloc выделяет n объектов размером m и заполняет их нулями, а realloc позволяет изменить размер ранее выделенной памяти и получает в качестве аргументов старый указатель и новый размер памяти в байтах.

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

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

В листинге 2.8 приведены примеры использования функций malloc, calloc, free, `realloc.

int *p=NULL;
p=(int*)
malloc(100); //выделяем 100 байт для хранения целых чисел
p=(int*)
realloc(p, 200); //выделяем 200 байт для хранения целых чисел
int count;
cin >> count; //вводим количество чисел, под которые нужно выделить память
int *a=(int*)
calloc(count, sizeof(int)); //создаём целые числа и присваиваем им всем 0
free(p); //освобождаем память
free(a);

Листинг 2.8 – Пример использования функций malloc, calloc, free, realloc.

С++ содержит два оператора, выполняющих выделение и освобождение памяти более эффективно и более просто, чем malloc и free. Этими операторами являются new и delete.

Оператор new выделяет память для хранения значения определённого типа и возвращает ее адрес. С помощью new могут быть размещены любые типы данных. Есть ряд преимуществ использования new перед использованием malloc(). Во-первых, оператор new автоматически вычисляет размер необходимой памяти. Нет необходимости в использовании оператора sizeof(). Более важно то, что он предотвращает случайное выделение неправильного количества памяти. Во-вторых, оператор new автоматически возвращает указатель требуемого типа, так что нет необходимости в использовании оператора преобразования типа. В-третьих, имеется возможность инициализации объекта при использовании оператора new.

Оператор delete освобождает память, на которую ссылается переменная указатель. Его следует использовать только для указателей на память, выделенную с использованием оператора new. Использование оператора delete с другими типами адресов может породить серьезные проблемы.

С помощью оператора new можно создать динамический массив. Динамические массивы дают возможность более гибкой работы с данными, так как позволяют не прогнозировать хранимые объёмы данных, а регулировать размер массива в соответствии с реально необходимыми объёмами. Возможность изменения размера отличает динамический массив от статического, размер которого задаётся на момент компиляции программы (см. листинг 2.9).

int count;
cin >> count; //ввод количества элементов массива
int *p = new int[count]; //создание динамического массива
delete [] p; //удаление динамического массива

Листинг 2.9 – Создание и удаление динамического массива.

ЗАКЛЮЧЕНИЕ

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

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

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

Список источников

  1. Управление памятью компьютера [Электронный ресурс] URL: http://www.psciences.net/main/sciences/computer_sciences/articles/upravleniye-pamatyu.html (дата обращения 09.05.2019).
  2. Внутри менеджера памяти. Выбор, подходы и реализация методов динамического выделения памяти [Электронный ресурс] URL: http://www.realcoding.net/article/view/2747 (дата обращения 12.05.2019).
  3. Лекция 2 | 1.2.1 Модель памяти С++ [Электронный ресурс] URL: http://uii.mpei.ru/study/courses/sdt-2013/files/lections/Lection_02_Memory .pdf (дата обращения 01.05.2019).
  4. Джефф Элджер | Часть 4. Управление памятью [Электронный ресурс] URL: http://ermak.cs.nstu.ru/cbooks/Eldger.pdf (дата обращения 02.05.2019).
  5. Глобальные переменные | Программирование на C и C++ [Электронный ресурс] URL: http://www.c-cpp.ru/books/globalnye-peremennye (дата обращения 10.05.2019).
  6. Основные принципы программирования: стек и куча [Электронный ресурс] https://tproger.ru/translations/programming-concepts-stack-and-heap (дата обращения 02.05.2019).
  7. Шилдт, Г. Полный справочник по С++ 4-ое издание / Г. Шилдт - М.: Вильямс, 2005. – 704 c.
  8. Динамическое выделение памяти в Си [Электронный ресурс] URL: https://learnc.info/c/memory_allocation.html (дата обращения: 09.05.2019).
  9. Работа с памятью с помощью new и delete [Электронный ресурс] URL: http://www.c-cpp.ru/books/rabota-s-pamyatyu-s-pomoshchyu-new-i-delete (дата обращения 09.05.2019).

Приложения