- •1.1.Указатели
- •1.1.1.Объявление указателей и работа с адресами
- •1.1.2.Адресная арифметика
- •1.1.3.Указатели как формальные параметры функции
- •1.1.4.Связь указателей и массивов
- •1.1.5.Константные указатели и указатели на константы
- •1.1.6.Связь указателей и строк
- •1.1.7.Преобразование указателей
- •1.1.8.Бестиповые указатели
- •1.1.9.Массив указателей
- •1.1.10.Указатели на указатели
- •1.1.11.Резервирование и освобождение памяти (операторы new и delete)
- •1.2.Типы данных, определяемые пользователем
- •1.2.1.Переопределяемые типы данных
- •1.2.2.Структуры
- •1.2.2.1.Массивы структур
- •1.2.2.2.Использование указателей на структуры
- •1.2.2.4.Динамические структуры и массивы структур
- •1.2.3.Классы
- •1.2.3.1.Введение в ооп
- •2.1.Потоки
- •2.2.Стандартные потоки
- •2.4.Файловые потоки
- •2.4.1.Общие принципы работы с файлами
- •2.4.2.Режимы открытия файлов
- •2.4.3.Текстовые и бинарные файлы и файловые потоки
- •2.4.4.Проверка состояния файла и потока
- •2.4.5.Функции (методы) ввода-вывода
- •2.4.5.1.Методы get и put
- •2.4.5.2.Метод getline
- •2.4.5.3.Методы read и write
- •2.4.6.Особенности работы с бинарными файлами
- •2.4.7.Использование текущей позиции файла
1.1.9.Массив указателей
Указатели, как и любые другие данные одного и того же типа, могут объединяться в массивы. Массивы указателей можно использовать для работы со всеми типами данных, но целесообразнее применять их для хранения символьных строк.
Объявление
char *ch[10];
означает, что ch – одномерный массив из 10 элементов, являющихся указателями на данные типа char.
Определить и инициализировать массив строк (массив указателей на данные типа char) можно, например, с помощью объявления
char* s[5] = { "one", "two", "three", "four", "five" };
или равнозначного ему
char* s[] = { "one", "two", "three", "four", "five" };
Оба приведенных в примере объявления создают массив указателей s и инициализируют его элементы адресами константных строк. Так как s – массив, указатель s будет содержать адрес начального элемента этого массива, в данном случае – указателя s[0], а каждый из указателей s[0], … , s[4] – адрес начала соответствующей строки. Соответственно, *s, как и s[0], будет означать адрес начала первой по счету строки массива s, а s[0][0], *s[0] (приоритет операции [] выше, чем приоритет операции *) и **s – значение ее начального символа.
1.1.10.Указатели на указатели
Поскольку массив в С++ реализован как указатель на его элементы, то массив указателей неявно определяет новый производный тип данных – указатель на указатель.
Объявление
int **p;
говорит о том, что p – указатель на указатель, то есть содержит адрес другого указателя, а операция *, дважды примененная к указателю p, даст значение типа int. Понятно, что можно определить указатель на указатель на указатель и т.д., то есть переменные с адресами могут образовывать некоторую иерархию (быть многоуровневыми). Например,
int ***v;
определяет указатель на указатель на указатель.
1.1.11.Резервирование и освобождение памяти (операторы new и delete)
Основное назначение указателей состоит в обеспечении возможности работы с динамическими (создаваемыми в процессе выполнения программы) переменными встроенных и производных типов. При такой работе указатели используются как средство хранения адресов резервируемых и освобождаемых участков памяти. Сами же действия, связанные с резервированием и освобождением памяти, осуществляют операторы new и delete.
Оператор new резервирует в области динамической памяти запрошенный участок памяти и возвращает его адрес, а оператор delete освобождает зарезервированный ранее участок памяти. Возможный формат этих операторов:
идентификатор = new тип ;
delete идентификатор ;
Здесь идентификатор – имя указателя, получающего в качестве значения адрес выделенной памяти, достаточной для размещения данных указанного типа. Пример:
int *ip; // указатель на целое значение типа int
...
ip = new int; // резервирование памяти для целого числа
...
delete ip; // освобождение памяти, адресуемой указателем ip
После выполнения последнего оператора примера память, выделенная для переменной типа int и адресуемая указателем ip, становится свободной (недоступной для использования, но доступной для нового резервирования оператором new). Переменная-указатель ip по-прежнему может использоваться в программе, но в текущий момент времени его значение становится неопределенным, указывающим на недоступный участок памяти.
Объявление указателя и резервирование памяти можно объединить:
int *ip = new int; // объявление и инициализация указателя на целое число
Приведенное объявление инициализирует переменную-указатель ip адресом выделенного ей участка памяти для переменной типа int, при этом значение данного участка памяти не определено. Существует возможность применения оператора new с инициализацией выделяемого участка памяти соответствующим значением. Формат оператора new при такой инициализации:
идентификатор = new тип ( значение );
например:
int *ip = new int(10); // выделение участка памяти для целого числа
// и его инициализация значением 10
Последний оператор объявляет переменную-указатель ip, резервирует требуемый участок памяти, присваивает этот адрес объявленной переменной и записывает по данному адресу начальное значение 10.
Для резервирования памяти для одномерного массива следует использовать оператор new формата
идентификатор = new тип [ размер ]
а для его освобождения – оператор delete формата
delete [] идентификатор ;
например:
int *p = new int[5]; // выделение памяти для массива из пяти целых чисел
...
delete []p; // освобождение памяти, зарезервированной для массива p
Наличие квадратных скобок указывает на необходимость освобождения памяти, занимаемой массивом, а не одной переменной.
Размер участка памяти, выделяемой для одномерного массива, может задаваться как целочисленным константным выражением, так и выражением, содержащим переменные, например:
int n;
...
cin >> n;
int *p = new int[n]; // выделение памяти для массива из n целых чисел
Инициализация элементов массива, определяемого посредством оператора new, в C++ не предусмотрена.
Не следует забывать о различиях в использовании квадратных и круглых скобок при работе с динамической памятью. Так, оператор:
int *p = new int[n];
осуществляет выделение памяти для массива из n целых чисел, а похожий оператор
int *p = new int(n);
– выделение памяти для одной переменной целого типа с начальным значением n.
С помощью оператора new можно выделять память и для многомерных (в частности, двумерных) массивов и массивов указателей.
Для освобождения памяти, динамически (с помощью оператора new) выделенной под двумерный массив a, следует использовать тот же, что и для одномерного массива, оператор
delete []a;
Поскольку при создании динамических массивов всего одна размерность может быть переменной, использование динамических двумерных массивов целесообразно, когда их необходимость заранее неизвестна и зависит от каких-то внешних условий.
Следует подчеркнуть, что динамически (с помощью оператора delete) можно освободить только ту память, которая была динамически (с помощью оператора new) получена.
