- •Аннотация
- •Предисловие
- •Г л а в а 1. Введение в указатели
- •§1. Понятие указателя. Операции разыменования и разадресации
- •§2. Инициализация и присваивание указателей
- •§3. Передача параметров функций с помощью указателей.
- •§4. Распределение динамической памяти.
- •4.1. Операция new
- •4.2. Операция delete
- •Упражнения, тесты.
- •Указатели и массивы
- •§1. Связь указателей и массивов
- •1.1. Указатели и одномерные массивы
- •1.2. Указатели и матрицы
- •Int MyFun (int *X, int n)
- •§3. Операции над указателями при работе с массивами.
- •3.1. Арифметические операции
- •3.2. Операции сравнения.
- •§4. Использование операций над указателями при работе с одномерными массивами
- •4.1. Использование индексов
- •4.2. Указатель в качестве параметра цикла
- •4.3. Использование указателя и индекса
- •§5. Строки.
- •5.1. Общая характеристика строк.
- •5.2. Примеры алгоритмов работы со строками.
- •5.3. Анализ строковых функций.
- •§6. Использование операций над указателями при работе со статической матрицей.
- •Упражнения, тесты.
- •Массивы указателей
- •§1. Статический массив указателей
- •§2. Частично динамическая матрица.
- •Int arr_of_size[n];
- •§3. Статический массив строк
- •§4. Динамический массив указателей
- •4.1. Указатель на указатель
- •4.2. Динамические “матрицы”.
- •Int *arr_of_size;
- •4.3. Передача матрицы в функцию
- •Int a[10]; FunArr1(a, 10,…);
- •Упражнения, тесты.
- •Задачи второго среднего уровня.
- •Структуры и другие типы, определяемые пользователем
- •§1. Структуры
- •Объявление структуры
- •1.2. Работа со структурой.
- •1.3. Вложенные структуры и статические массивы в структурах
- •1.4. Статический массив структур
- •§2. Cтруктуры и указатели
- •2.1. Указатели в структуре.
- •2.2. Указатели на структуру
- •2.3. Динамический массив структур
- •2.4. Ссылка на структуру.
- •2.5. Указатели и вложенные структуры
- •§3. Cтруктуры и функции
- •3.1. Передача полей структуры в функцию.
- •Void MyFun1 (int X, float &y, int *u1, float *u2, char *s);
- •3.2. Передача всей структуры в функцию
- •Void Fun1 (tst s,…);
- •Void Fun2 (tst & s,…);
- •Void Fun3 (tst* s,…);
- •§4. Cтруктуры и классы.
- •§5. Объединения.
- •Представление вещественных чисел в памяти компьютера.
- •§6. Поля битов (битовые поля)
- •Ввод в ы в о д
- •Symbol Code16 Code10 Code2
- •§7. Перечисления
- •Какие из строк (//1 – //9) правильные?
§4. Динамический массив указателей
4.1. Указатель на указатель
При работе с динамическим массивом указателей и, как частный случай, с динамическими матрицами, в объявлении будут использоваться два подряд идущих символа “*”, что означает указатель на указатель. Поэтому параграф начнём с рассмотрения этого понятия.
Как мы знаем, обычный указатель содержит адрес некоторого участка памяти, в котором находится некоторое значение. В случае указателя на указатель, первый из них содержит адрес ячейки, в которой находится не обрабатываемая информация, а в свою очередь адрес другого участка памяти, содержащего некоторое значение. В некоторых книгах это называют многочисленным перенаправлением или цепочкой указателей.
Переменная, являющаяся указателем на указатель, описывается с использованием двух символов “*” перед именем. Например, объявление
float **p2;
означает, что p2 — это не указатель на число с плавающей точкой, а в p2 будет храниться адрес ячейки, в которой будет находиться адрес другой ячейки с вещественным числом. Такую переменную можно кратко назвать как указатель на указаетль на вещественное число.
Для получения доступа к такой информации, то есть у нас к вещественному числу, необходимо дважды применить операцию “*”. Например, рассмотрим следующий фрагмент:
float x, *p1, **p2;
x=2.2; p1=&x; p2=&p1;
printf (“%5.1f %5.1f%5.1f”, x,*p1,**p2);
В результате будет трижды выведено число 2.2, доступ к которому осуществляется с помощью простой переменной x, операции разыменования для указателя на вещественное число p1 и дважды используемой операции разыменования для указателя на указатель p2.
Мы уже знаем, что cout<<p1; выведет адрес ячейки памяти, в которой находится вещественное число x=2.2. Аналогично в p2 хранится адрес ячейки p1, в которой адрес вещественного числа. А что получится в результате выполнения cout<<(*p2);? Будет выведено содержимое ячейки памяти, адрес которой в p2, то есть значение p1.
Многочисленное перенаправление теоретически может и дальше расширяться, то есть в объявлении разрешается записывать более двух звёздочек. Но это на практике встречается не так часто. Излишнее перенаправление может приводить к трудно исправляемым концептуальным ошибкам.
4.2. Динамические “матрицы”.
Динамическая “матрица” (или в отличие от частично динамической назовём её полностью динамичсеской) с одинаковым количеством элементов в строках создаётся следующим образом.
1) Динамическая “матрица” объявляется как указатель на указатель, или, лучше, как динамический массив указателей. Для объяснения этого проведём сначала аналогию с обычным одномерным массивом, например int a[10]. Для создания динамического одномерного массива его надо объявить, заменив размерность в квадратных скобках на символ “*”, который записывается после типа перед именем: int *d; и выполнить операцию d=new int [m], где m —константа или переменная.
Так как статический массив указателей объявляется, например, как int *ap[5], то динамический массив указателей объявляем, заменив формально [5] на звёздочку и записав её перед именем, то есть с помощью двух звёздочек, как было рассмотрено в предыдущем пункте:
int **D;
2) Мы знаем, что из объявления int *d не следует ещё, что в d будет адрес начала массива, может быть адрес простой целочисленной переменной. Точно также из объявления int **D; не видно, что в D будет адрес начала массива указателей. Это зависит от операции new. Если выполнить
D=new * int;
то выделяется память для размещения одного адреса на область памяти, в которой будет одно или несколько целых чисел. Нас такой вариант не устраивает, так как нам надо получить и сохранить в памяти адреса начала каждой строки матрицы. Чтобы зарезервировать память для массива указателей, в операции new после типа (* int) надо записать количество элементов данного типа, то есть количество указателей, которое также может быть как константой, так и переменной. Пусть int n; cin>>n; Тогда D=new * int[n]; выделяет память для размещения n указателей, и адрес начала этой области помещается в D . На рисунке этому этапу соответствует одна левая стрелка, над которой записано 2).
3) Теперь необходимо определить, что будет содержать каждый элемент этого массива указателей. Как и для частично динамической матрицы, определяем количество элементов в каждой строке. Пусть оно одинаковое для каждой строки: int m; cin>>m;
4) Резервируем память для каждой строки “матрицы”, то есть для числового одномерного массива, и определяем значение каждого элемента массива указателей D[i]:
for (int i=0; i<n; i++) D[i]= new int[m];
Этот этап на рисунке изображён в виде нескольких горизонтальных стрелок, над которыми записана цифра 3).
Таким образом, процесс создания динамической “матрицы” можно изобразить в виде следующего рисунка:
5) После этого доступ к элементам такой “матрицы” можно выполнять, как и для обычной статической матрицы с помощью двух индексов: D[i][j]. Но при работе с созданной динамической “матрицей” необходимо правильно пользоваться указателями для организации циклов вместо индексов. Объяснение аналогично как для частично динамической “матрицы”.
6) Удапение динамической “матрицы” выполняется в обратном порядке следующим образом
6.1) Сначала освобождаем память, выделенную для каждой строки. Так как количество таких участков памяти, в каждом из которых размещалось m чисел, соответствует количеству строк, то это выполняем в цикле n раз:
for ( int i=0; i<n; i++) delete[]D[i];
Пустые квадратные скобки здесь означают, что в каждом элементе массива указателей D[i] был адрес не одного числа, а адрес одномерного массива, то есть адрес i-строки матрицы. Поэтому надо освобождать память для массива, то есть i-й строки матрицы
6.2) Освобождаем память, выделенную для массива указателей.
delete[]D;
Так как такой массив указателей был один, то это выполняется без цикла один раз. Пустые квадратные скобки означают, что освобождается память, выделенная для массива указателей.
Пример работы с такой “матрицей” приведен в лабораторной работе 5 (пример 1).
Как и для частично динамической “матрицы”, в строках полностью динамической матрицы может быть различное количество элементов. Здесь показан один из вариантов создания такой “матрицы”.
Объявляем указатель для динамического одномерного массива, в котором будут хранниться количество элементов в каждой строке.