- •Содержание
- •Введение
- •Указатель
- •1.1. Определение указателя
- •1.2. Понятие динамической структуры данных
- •1.3.Переменные типа "массив". Арифметические операции с указателями
- •Int hours[6];
- •1.4. Динамические массивы
- •Контрольные вопросы
- •Задания для практической работы
- •Абстрактный тип данных
- •Линейный однонаправленный (односвязный ) список
- •1.1. Определение односвязного списка
- •1.2. Операции обработки списка
- •1.3. Создание класса для работы со списком
- •1.4. Реализация копирования списка с помощью класса
- •Контрольные вопросы
- •Задания для практической работы
- •Линейные двунаправленные списки
- •1.1. Определение двунаправленного списка
- •Контрольные вопросы
- •Задания для практической работы
- •Структуры данных стеки и очереди
- •1.1. Определение стека, очереди
- •1.2. Представление стека
- •1.3. Основные операции над стеком
- •1.4. Реализация стека на основе статического массива
- •1.5. Реализация стека с использованием динамической памяти
- •1.6. Представление статической очереди
- •1.7.Представление динамической очереди
- •Контрольные вопросы
- •Задания для практической работы
- •Структуры данных: деревья
- •1.1. Определение структуры: дерево
- •1.2. Обходы деревьев
- •1.2.1. Алгоритмы обхода в глубину
- •1.2.2. Алгоритмы обхода в ширину
- •1.3. Ввод дерева
- •1.4. Разрушение дерева
- •1.5. Представление выражений с помощью деревьев
- •Контрольные вопросы
- •Задания для практической работы
- •Литература
1.3.Переменные типа "массив". Арифметические операции с указателями
В Си++ понятия массива и указателя тесно связаны. Рассмотрим оператор описания:
Int hours[6];
Этот массив состоит из 6-ти элементов:
hours[0] hours[1] hours[2] hours[3] hours[4] hours[5]
Массивы в Си++ реализованы так, как будто имя массива (например, "hours") является указателем. Поэтому, если добавить в программу объявление целочисленного указателя:
int* ptr;
то ему можно присвоить адрес массива (т.е. адрес первого элемента массива):
ptr = hours;
После выполнения этого оператора обе переменные – "ptr" и "hours" – будут указывать на целочисленную переменную, доступную в программе как "hours[0]".
Фактически, имена "hours[0]", "*hours" и "*ptr" являются тремя различными именами одной и той же переменной. У переменных "hours[1]", "hours[2]" и т.д. также появляются новые имена:
*(hours + 1) *(hours + 2) ...
или
*(ptr + 1) *(ptr + 2) ...
В данном случае "+2" означает "добавить к адресу указателя смещение, соответствующее 2-м целым значениям". Из арифметических операций к указателям часто применяется сложение и вычитание (в том числе операции инкремента и декремента"++" и "--"), а умножение и деление не используются. Значения однотипных указателей можно вычитать друг из друга.
Главное, что нужно запомнить относительно сложения и вычитания значений из указателя – в выражениях Си++ указывается не число, которое нужно вычесть (или добавить) из адреса, а количество переменных заданного типа, на которые нужно "сместить" адрес.
Арифметические выражения с указателями иногда позволяют более кратко записать обработку массивов. В качестве примера рассмотрим функцию для преобразования английской строки в верхний регистр:
Пример 1.5:
void ChangeToUpperCase( char phrase[ ] )
{
int index = 0;
while ( phrase[index] != '\0' )
{
if ( LowerCase(phrase[index]) )
ChangeToUpperCase( phrase[index] );
index++;
}
}
bool LowerCase( char character )
{
return ( character >= 'a' && character <= 'z');
}
void ChangeToUpperCase( char& character )
{
character += 'A' - 'a';
}
Обратите внимание на полиморфизм функции "ChangeToUpperCase(...)" – при обработке вызова компилятор различает две перегруженных функции, т.к. у них разные параметры (у одной – параметр типа "char", а у другой – параметр типа "символьный массив"). Имя массива "phrase" является переменной-указателем, поэтому функцию с параметром-массивом можно переписать короче, если использовать арифметические выражения с указателями:
Пример 1.6:
void ChangeToUpperCase( char* phrase )
{
while ( *phrase != '\0' )
{
if ( LowerCase(*phrase) )
ChangeToUpperCase(*phrase);
phrase++;
}
}
Эта модификация функции не влияет на остальные части программы – вызовы вариантов функций с параметром-указателем или параметром-массивом записываются одинаково, например:
char a_string[] = "Hello World";
...
...
ChangeToUpperCase( a_string );
1.4. Динамические массивы
Правила создания и уничтожения динамических переменных типов "int", "char", "double" и т.п. распространяются и на динамические массивы. По отношению к массивам динамическое распределение памяти особенно полезно, поскольку иногда бывают массивы больших размеров.
Динамический массив из 10-ти целых чисел можно создать следующим образом:
int* number_ptr;
number_ptr = new int[10];
Для уничтожения динамического массива применяется оператор "delete" c квадратными скобками "[ ]":
delete[] number_ptr;
Скобки "[]" играют важную роль. Они сообщают оператору, что требуется уничтожить все элементы массива, а не только первый. Работа с динамическими массивами показана в приведенном фрагменте программы. У пользователя запрашивается список целых чисел, затем вычисляется и выводится на экран их среднее значение. ...
Пример 1.7:
...
int no_of_integers, *number_ptr;
cout << "Введите количество целых чисел в списке: ";
cin >> no_of_integers;
number_ptr = new int[no_of_integers];
if ( number_ptr == NULL )
{
cout << "Извините, недостаточно памяти.\n";
exit(1);
}
cout << "Наберите " << no_of_integers;
cout << " целых чисел, разделяя их пробелами:\n";
for ( int count = 0; count < no_of_integers; count++ )
cin >> number_ptr[count];
cout << "Среднее значение: ";
cout << average( number_ptr, no_of_integers );
delete[] number_ptr;
... Динамические массивы, как и обычные, можно передавать в качестве параметров функций.