- •1. Краткие теоретические сведения
- •1.1. Понятие указателя
- •1.2. Объявление указателя
- •1.3. Операции над указателями
- •1.4. Передача параметра по ссылке
- •1.5. Динамическое выделение памяти
- •1.5.1. Динамическое размещение одномерного массива
- •1.5.2. Динамическое размещение двухмерного массива
- •1.5.3. Динамические массивы
- •1.5.4. Формирование динамических массивов с использованием библиотечных функций
- •1.5.5. Формирование динамических массивов с использованием операций new и delete
- •1.5.6. Динамические массивы
- •1.5.7. Динамические массивы
- •1.5.8. Динамические массивы
- •1.5.9. Освобождение выделенной динамической памяти.
- •1.5.10. Описание динамической строки
- •1.5.11. Объявление динамического массива
- •1.6. Связь указателей и массивов
- •1.7. Массивы указателей
- •1.8. Массивы и функции
- •1.9. Указатель на символьную строку
- •Второй пример
- •Увеличение указателя на символьную строку
- •Уменьшение количества операторов
- •Сканирование символьной строки
- •1.10. Заполнение массивов случайными числами
- •1.12. Примеры программирования задач
- •2. Задание
- •2.4. Задания для выполнения на занятиях
- •2.4.1. Задание 1. Динамические одномерные массивы
- •2.4.1.1. Варианты заданий
- •2.4.1.2. Пример обработки динамического массива для варианта 30
- •2.4.1.3. Программа
- •Int *a, *b; // указатели исходных одномерных массивов a и b
- •2.4.1.4. Тестирование
- •2.4.2. Задание 2. Динамические двумерных массивы
- •2.4.2.1. Варианты заданий
- •2.4.2.2. Пример для варианта 30
- •2.4.2.3. Программа
- •2.4.2.4. Тестирование
- •2.4.3. Задание 3. Динамические одномерные массивы Викентьева
- •2.4.3.1. Варианты заданий
- •2.4.4.1. Варианты заданий
- •4. Требование к отчету
- •4. Краткие теоретические сведения.
- •5. Вопросы для самоконтроля
- •Литература
- •1. Краткие теоретические сведения 2
- •1.1. Понятие указателя 2
1.7. Массивы указателей
Указатели как и переменные любого другого типа, могут объединяться в массивы. Объявление массива указателей из 10 элементов имеет вид int *x[10];. Каждому из элементов массива можно присвоить адрес. Например, пятому элементу этого массива присвоим адрес целой и ранее объявленной переменной у: х[4]=&у;. Если затем необходимо найти значение переменной у, это можно сделать, используя конструкцию *х[4].
C помощью массива указателей несложно запрограммировать динамическое создание двумерных массивов. Объявим массив 10 указателей p:
int *p[10];
C каждым из них можно связать динамически созданный одномерный массив и интерпретировать его как строку матрицы:
for (i=0; i<=8; i++) //Цикл по строкам
{p[i]=new int[20];} // Создание строки.
В результате получили следующую замысловатую конструкцию. Идентификатор p является именем массива указателей, которые содержат адреса первых элементов других массивов — строк матрицы. Выражение p[k] или равносильное ему *(p+k) представляют собой адрес первого элемента строки с номером k. Выражение *(p[k]+m) или *(*(p+k)+m) — элемент с номером m строки с номером k, то есть элемент двумерного массива, расположенный в строке k и столбце m. Заметим также, что динамически созданные «строки матрицы» не обязательно должны иметь одинаковый размер, а само понятие «матрица» в данном контексте уместно заменить выражением «массив массивов».
Связь указателей и массивов с одним измерением справедливо и для массивов с бóльшим числом измерений.
Если рассматривать предыдущий массив (int a[2][3];) как массив двух массивов размерностью по три элемента каждый, то обращение к элементу а[i][j] соответствует эквивалентное выражение *(*(а+i)+j), а объявление этого массива с использованием указателей будет иметь вид
int **а;
Таким образом, имя двухмерного массива – ID указателя на указатель.
Указатели могут ссылаться на другие указатели. При этом в ячейках памяти, на которые будут ссылаться первые указатели, будут содержаться не значения, а адреса вторых указателей. Число символов * при объявлении указателя показывает порядок указателя. Чтобы получить доступ к значению, на которое ссылается указатель его необходимо разыменовывать соответствующее количество раз. Разработаем программу, которая будет выполнять некоторые операции с указателями порядка выше первого.
// pointer.cpp: определяет точку входа для консольного приложения.
#include "stdafx.h"
#include <iostream>
using namespace std;
int _tmain(int argc, _TCHAR* argv[])
{
int var = 123; // инициализация переменной var числом 123
int *ptrvar = &var; // указатель на переменную var
int **ptr_ptrvar = &ptrvar; // указатель на переменную var
int ***ptr_ptr_ptrvar = &ptr_ptrvar;
cout << " var\t\t= " << var << endl;
cout << " *ptrvar\t= " << *ptrvar << endl;
cout << " **ptr_ptrvar = " << **ptr_ptrvar << endl; // два раза разименовываем указатель, так как он второго порядка
cout << " ***ptr_ptrvar = " << ***ptr_ptr_ptrvar << endl; // указатель третьего порядка
cout << "\n ***ptr_ptr_ptrvar -> **ptr_ptrvar -> *ptrvar -> var -> "<< var << endl;
cout << "\t " << &ptr_ptr_ptrvar<< " -> " << " " << &ptr_ptrvar << " ->" << &ptrvar << " -> " << &var << " -> " << var << endl;
system("pause");
return 0;
}
На рис. 15. 3 показан результат работы программы.
Рис. 15. 3. Указатели в С++
Данная программа доказывает тот факт, что для получения значения количество разыменований указателя должно совпадать с его порядком. Логика n-кратного разыменования заключается в том, что программа последовательно перебирает адреса всех указателей вплоть до переменной, в которой содержится значение. В программе показана реализация указателя третьего порядка. И если, используя такой указатель (третьего порядка) необходимо получить значение, на которое он ссылается, делается 4 шага:
1. По значению указателя третьего порядка получить адрес указателя второго порядка;
2. По значению указателя второго порядка получить адрес указателя первого порядка;
3. По значению указателя первого порядка получить адрес переменной;
4. По адресу переменной получить доступ к её значению.
Данные четыре действия показаны на рисунке 3 (две предпоследние строки). Верхняя строка показывает имена указателей, а нижняя строка их адреса.
На рис. 15. 4 показана схема разыменовывания указателя третьего порядка из верхней программы. Суть в том, что указатели связаны друг с другом через свои адреса. Причём, например, для указателя ptr_ptrvar данное число 0015FDB4 является адресом, а для указателя ptr_ptr_ptrvar это же число является значением.
Рис. 15.4. Указатели в С++
Именно таким образом выполняется связка указателей. Вообще говоря, указатели порядка выше первого используются редко, но данный материал поможет понять механизм работы указателей.