
- •В.В. Чуркин технологии программирования
- •Содержание
- •Введение
- •Указатели. Операции над указателями Операции адресации и разыменования.
- •Арифметические операции.
- •Присваивание указателей.
- •Смещение и индексирование указателей.
- •Функции
- •Объявление функции (прототип)
- •Параметры функции
- •Встроенные функции
- •Функции с параметрами со значениями по умолчанию
- •Параметры функции main()
- •Рекурсивные функции
- •Перегрузка функций
- •Шаблоны функций
- •Указатели на функции
- •Объявление и инициализация массива указателей на функции:
- •Указатель на функцию как возвращаемое функцией значение
- •Выделение и освобождение динамической памяти
- •Символьные и строковые данные Символьные константы
- •Строковые константы (строки)
- •Символьные переменные
- •Строки – переменные
- •Специальные функции ввода-вывода строк
- •Стандартная библиотека функций языка с
- •Файлы Потоковый ввод-вывод в языке с Функции верхнего уровня файлового ввода-вывода
- •Открытие и закрытие потока
- •Текстовый режим
- •Бинарный режим
- •Закрытие файла
- •Функции в языке c для работы с файлами
- •Двоичный (бинарный) режим обмена с файлами
- •Строковый обмен с файлами
- •Форматный обмен с файлами
- •Позиционирование в потоке
- •Ввод-вывод нижнего уровня
- •Открытие / закрытие файла
- •Чтение и запись данных
- •Произвольный доступ к файлу
- •Позиционирование файлов
- •Сортировки числовых массивов Принцип наименьших привилегий
- •Обменная сортировка (SwapSort)
- •Сортировка выбором (SelectSort)
- •Пузырьковая сортировка (BubbleSort)
- •Сортировка вставками (InsertSort)
- •Быстрая сортировка (QuickSort)
- •Поиск в числовых массивах
- •Структуры
- •Форматы определения структурных типов
- •Форматы определения объектов структурных типов
- •Операции над объектами структурного типа
- •Доступ к элементам объектов структурного типа
- •Структуры, массивы и указатели
- •Объединения (смеси)
- •Оператор switch (переключатель)
- •Динамические структуры данных
- •Реализация стека с помощью массива
- •Очередь
- •Очередь приоритетов
- •Реализация очереди с помощью массива
- •Линейные списки
- •Функции для работы с двунаправленным линейным списком
- •Реализация списка с помощью массивов
- •Поиск хэшированием
- •Бинарные деревья
- •Бинарное упорядоченное дерево (дерево поиска)
- •Идеально сбалансированное дерево
- •Операции с бинарным упорядоченным деревом
- •Удаление узла из дерева
- •Обход (просмотр) дерева
- •Реализация дерева с помощью массивов
- •Вывод динамических структур в файл и чтение их из файла
- •Сбалансированные (avl) деревья
- •Алгоритм avl-вставки.
- •Повороты
- •Классы и объектно-ориентированное программирование
- •Объявление класса
- •Определение класса (реализация класса)
- •Использование класса (драйвер класса)
- •Доступ к элементам класса
- •Отделение интерфейса от реализации
- •Обслуживающие функции-утилиты
- •Конструкторы
- •Windows-программы в Builder
- •Структура головного файла проекта
- •Структура заголовочного файла модуля формы (“Unit1.H”)
- •Структура файла реализации модуля формы (“Unit1.Cpp”)
- •Области видимости (или области действия) переменных в блоках. Время жизни переменных
- •Доступ к свойствам и функциям-элементам (методам) объектов, переменным и функциям в приложении, содержащем одну форму
- •Константные объекты и константные функции-элементы
- •Перегрузка операций
- •Перегрузка унарных операций
- •Перегрузка бинарных операций
- •Перегрузка операции присваивания
- •Перегрузка операции приведения типа
- •Перегрузка операции индексирования []
- •Композиция классов
- •Дружественные функции класса
- •Дружественный класс
- •Использование указателя this
- •Статические элементы класса
- •Шаблон класса для статически и динамически создаваемых объектов
- •Конструктор 1
- •Деструктор
- •Вызовы конструкторов и деструкторов
- •Перегруженная операция присваивания
- •Конструктор 2 (конструктор копирования, конструктор копии)
- •Наследование. Иерархия классов.
- •Ключи доступа
- •Пример простого наследования (точка, круг)
- •Правила наследования функций-элементов. Вызовы конструкторов и деструкторов в иерархии
- •Виртуальные функции и полиморфизм
- •Правила определения и наследования виртуальных функций
- •Позднее (динамическое) связывание
- •Полиморфизм. Абстрактные и конкретные классы
- •Учебная литература (основная)
- •Учебная литература (для углубленного изучения)
- •Учебно-методические издания
Выделение и освобождение динамической памяти
Динамическая память используется тогда, когда велик объем данных, или он становится известным лишь во время выполнения программы.
В файле stdlib.h находятся следующие функции для выделения и освобождения динамической памяти в языке C.
void* malloc(unsigned s); - возвращает указатель на начало выделенной области памяти длиной в s байт; при неудаче возвращает NULL (значение константы NULL равно нулю).
unsigned – это тип unsigned int – два байта, 0..65535.
Пример. char* b; b=(char*)malloc(80); - выделена динамическая память под 80 символов.
void* calloc(unsigned n, unsigned m); - возвращает указатель на начало области обнуленной динамической памяти, выделенной для размещения n элементов по m байт каждый; при неудаче возвращает NULL.
void* realloc(void* b, unsigned ns); - изменяет размер ранее выделенной памяти до ns байт. Если b=NULL (память не выделялась), то выполняется как malloc.
void free(void*b); - освобождает ранее выделенную память, адресуемую b.
В языке C++ динамическое распределение памяти для любых стандартных или пользовательских типов реализуется операциями new и delete.
Операция new автоматически создает объект соответствующего размера и возвращает указатель соответствующего типа. Если new не может найти необходимое пространство в памяти, она возвращает указатель, равный нулю.
int* iptr; iptr=new int;
Объявлен указатель iptr на целое и по этому указателю выделяется динамическая память под целое (лишено практического смысла). Адрес выделенного под целое участка памяти заносится в iptr.
Динамическая память освобождается также по указателю:
delete iptr;
В C++ можно использовать инициализатор для только что созданного в динамической памяти объекта : float* fp = new float(3.14159);
int* ptr; ptr=new int[100];// можно int* ptr=new int[100];
Объявлен указатель ptr на целое и по этому указателю выделяется непрерывный участок динамической памяти под массив на 100 целых чисел. Адрес выделенного участка, т.е. адрес первого его байта, или &ptr[0], заносится в ptr.
Динамическая память, выделенная под массив, освобождается оператором delete[]ptr; При записи delete ptr; динамическая память освобождается только под первым элементом массива, т.е. ptr[0];
Контроль за правильностью выполнения операции выделения динамической памяти осуществляется с помощью макроса assert, определенного в файле <assert.h>. Например,
int n,vptr; // n – размер массива
------------ инициализация n -------------
vptr = new int[n];
assert(vptr!=0);
// инициализация элементов массива случайными числами
// в диапазоне значений от -9 до 9
for(int i=0: i<n; i++)
vptr[i]= (rand()-16383)%10;
--------- обработка динамического массива --------
delete[]vptr;
vptr=0;
Операция new выделяет непрерывный участок динамической памяти под массив длиной n, для целых чисел, и возвращает адрес этого участка, т.е. адрес элемента массива с индексом 0 – vptr[0]. Если память не выделена, то значение указателя vptr окажется равным нулю, а значение выражения vptr!=0 – ложным (нулевым). Тогда макрос assert прерывает выполнение программы. При завершении работы выводится сообщение об ошибке вида: Assertion failed: expression, file <имя файла>, line <номер строки>. После того, как отладка программы будет закончена и макросы assert будут больше не нужны, в начале программы достаточно добавить строку #define NDEBUG вместо того, чтобы удалять в программе каждый макрос assert вручную. Операция delete объявляет участок памяти под массивом свободным. После применения операции delete указатель рекомендуется обнулять, с целью защиты программного продукта.
Выделение и освобождение динамической памяти для размещения двумерного массива (матрицы) выполняется, например, следующим образом.
int n, m, **nptr; // n – число строк, m – число столбцов матрицы
------------ инициализация n, m -------------
// выделение памяти под массив указателей на строки
nptr=new int*[n];
assert(nptr!=0);
for(int i=0; i<n; i++) {
// выделение памяти под i – ю строку
nptr[i]=new int[m];
assert(nptr[i]!=0);
for(int j=0; j<m; j++)
// инициализация строк матрицы случайными
// числами в диапазоне значений от -99 до 99
nptr[i][j] = (rand()-16383)%100;}
------------- обработка динамической матрицы ------------
for(int i=0: i<n; i++)
// освобождение памяти под i – й строкой
delete[]nptr[i];
// освобождение памяти под массивом указателей на строки
delete[]nptr;
nptr=0;
Возможен и другой вариант, когда память выделяется под столбцы. В этом случае приведенный выше фрагмент программы будет выглядеть следующим образом.
int n, m, **mptr; //n – число строк, m – число столбцов матрицы
------------ инициализация n, m -------------
// выделение памяти под массив указателей на столбцы
mptr=new int*[m];
assert(mptr!=0);
for(int j=0; j< m; j++) {
// выделение памяти под j – й столбец
mptr[j]=new int[n];
assert(mptr[j]!=0);
for(int i=0: i<n; i++)
// инициализация столбцов матрицы случайными
// числами в диапазоне значений от -99 до 99
mptr[j][i]= (rand()-16383)%100; }
------------- обработка динамической матрицы ------------
for(int j=0; j<m; j++)
// освобождение памяти под j– м столбцом
delete[]mptr[j];
// освобождение памяти под массивом указателей на столбцы
delete[]mptr;
mptr=0;