Основы программирования на C++
Максименкова Ольга Вениаминовна
Старший преподаватель Департамента программной инженерии Факультета компьютерных наук
© Максименкова О.В., Незнанов А.А., ДПИ и ДАДиИИ
ФКН НИУ ВШЭ128.06.2016
Цели занятия
© Максименкова О.В., Незнанов А.А., ДПИ и ДАДиИИ
ФКН НИУ ВШЭ2
Рассмотреть
• Массивы в языке С++
• Указатели в языке С++
• Связь между массивами и указателями
• Массивы и указатели как параметры функций
• Некоторые алгоритмы сортировки массивов
Попрактиковаться
• В реализации алгоритмов на языке C++
28.06.2016
Производные типы данныхМассивы
Указатели
© Максименкова О.В., Незнанов А.А., ДПИ и ДАДиИИ
ФКН НИУ ВШЭ328.06.2016
Массивы
© Максименкова О.В., Незнанов А.А., ДПИ и ДАДиИИ
ФКН НИУ ВШЭ4
int arr[]; // нельзя определить массив без размера
тип имя_массива[константое_выражение]
int arr[10]; // определён массив из 10 элементов
int arr[10]; // определён массив из 10 элементовfor (int i = 0; i < 10; i++) {cout << arr[i] << " ";cout << arr + i << "\n";
}
-858993460 000000308B0FFCA8-858993460 000000308B0FFCAC-858993460 000000308B0FFCB0-858993460 000000308B0FFCB4-858993460 000000308B0FFCB8-858993460 000000308B0FFCBC-858993460 000000308B0FFCC0-858993460 000000308B0FFCC4-858993460 000000308B0FFCC8-858993460 000000308B0FFCCC
28.06.2016
Инициализация элементов массива
© Максименкова О.В., Незнанов А.А., ДПИ и ДАДиИИ
ФКН НИУ ВШЭ5
int arr[10];for (int i = 0;i < 10;i++) {
arr[i] = 0;cout << arr[i] << " ";
}
short shArr[10];shArr[0] = 1;for (int i = 1; i < 10; i++) {shArr[i] = shArr[i-1]*i;cout << i << " " << shArr[i] << " ";cout << shArr + i << "\n";
}
0 00000019F88FFA380 00000019F88FFA3C0 00000019F88FFA400 00000019F88FFA440 00000019F88FFA480 00000019F88FFA4C0 00000019F88FFA500 00000019F88FFA540 00000019F88FFA580 00000019F88FFA5C
1 1 000000A7C63AFA9A2 2 000000A7C63AFA9C3 6 000000A7C63AFA9E4 24 000000A7C63AFAA05 120 000000A7C63AFAA26 720 000000A7C63AFAA47 5040 000000A7C63AFAA68 -25216 000000A7C63AFAA89 -30336 000000A7C63AFAAA
Переполнение типа
28.06.2016
Темная сторона скрывает все. Невозможно будущее предвидеть нам ©
© Максименкова О.В., Незнанов А.А., ДПИ и ДАДиИИ
ФКН НИУ ВШЭ6
C++ Accesses an Array out of bounds gives no error, why?[http://stackoverflow.com/questions/1239938/c-accesses-an-
array-out-of-bounds-gives-no-error-why]
int arr[5]; // определён массив из 5 элементов
// заполним массив десятьюfor (int i = 0; i < 10; i++) {arr[i] = i;cout << arr[i] << " ";
}system("pause");return 0;
28.06.2016
Основные задачи обработки массивов
• Заполнение и считывание в определённом порядке
• Упорядочение элементов по их значениям
• Сортировка на месте (в том же массиве)
• Создание оглавления (индексный массив)
• Алгоритмы переупорядочивания по оглавлению
• Информационный поиск
• Линейный поиск по совпадению
• Бинарный поиск по совпадению или по близости
• Поиск экстремумов
© Максименкова О.В., Незнанов А.А., ДПИ и ДАДиИИ
ФКН НИУ ВШЭ728.06.2016
Случайные числа в C++
© Максименкова О.В., Незнанов А.А., ДПИ и ДАДиИИ
ФКН НИУ ВШЭ8
Сделаем работу с массивами удобнее. Будем заполнять их случайными числами
// получение случайного числа из диапазона от A до Bint A = 10, B = 11;cout << "Random value " << A + rand() % (B - A + 1) << "\n";// заполним массивfor (int i = 0; i < 10; i++) {arr[i] = rand() % 100; // значение от 0 до 99cout << arr[i] << " ";
}
Array_Snippets_02
Массив будет заполнен, но при перезапуске программы числа
изменяться не будут
// инициируем рандомизацию чиселsrand(time(0));
28.06.2016
Заполнение массива случайными числами
© Максименкова О.В., Незнанов А.А., ДПИ и ДАДиИИ
ФКН НИУ ВШЭ9
#include <iostream>#include <ctime>using namespace std;int main() {int arr[10]; // определён массив из 10 элементов// инициируем рандомизацию чиселsrand(time(0));cout << "RAND_MAX = "<< RAND_MAX << "\n"; // значение константы RAND_MAX// получение случайного числа из диапазона от A до Bint A = 10, B = 11;cout << "Random value " << A + rand() % (B - A + 1) << "\n";// случайное вещественное значение (0, 1)cout << "Random double " << (double)rand() / RAND_MAX << "\n";// заполним массивfor (int i = 0; i < 10; i++) {arr[i] = rand() % 100; // значение от 0 до 99cout << arr[i] << " ";
}return 0;
}
Генератор случайных чисел rand() в С++(http://cppstudio.com/post/339/)
Новые генераторы случайных чисел(ГСЧ) из С++11 (http://www.quizful.net/post/random-number-generation-in-cpp11)
28.06.2016
Указатели
© Максименкова О.В., Незнанов А.А., ДПИ и ДАДиИИ
ФКН НИУ ВШЭ10
тип *имя_указателя int *ptr1;double *ptr2;
Операция
разыменовывания
тип *имя_указателя = инициализирующее_выражение;тип *имя_указателя (инициализирующее_выражение);
Описание переменной с типом указателя
Формы записи инициализатора
double x = 3;int *ptr1(nullptr);double *ptr2;ptr2 = &x; // указатель связан с адресом x
28.06.2016
© Максименкова О.В., Незнанов А.А., ДПИ и ДАДиИИ
ФКН НИУ ВШЭ11
double x = 3;int *ptr1(nullptr);double *ptr2;*ptr2 = 44; // Не компилируется. Почему?cout << *ptr2;
double x = 3;int *ptr1(nullptr);double *ptr2;*ptr1 = 44; // Так компилируется, но тоже не работает
double x = 3;int *ptr1(nullptr);double *ptr2;ptr2 = &x; // указатель связан с адресом xcout << *ptr2; // получение значение по адресу
28.06.2016
Операции над указателями. Арифметика указателей
© Максименкова О.В., Незнанов А.А., ДПИ и ДАДиИИ
ФКН НИУ ВШЭ12
int x;int *ptr = &x;cout << ptr << " " << ptr++ << "\n";cout << ptr - 2 << "\n";cout << ptr + sizeof(int) * 4 << "\n";
0076FDD8 0076FDD40076FDD00076FE18
• операция разыменования (*)
• приведение типов
• присваивание
• получение адреса (&)
• аддитивные операции
• инкремент/декремент
• операции сравнения
28.06.2016
Массивы и указатели
© Максименкова О.В., Незнанов А.А., ДПИ и ДАДиИИ
ФКН НИУ ВШЭ13
for (int i = 0;i < 10;i++) {arr[i] = i;cout << arr[i] << " ";
}cout << "\nExperiments!!!\n";for (int i = 0;i < 10;i++) {
*(arr + i) = 9 - i;cout << arr[i] << " ";
}
0 1 2 3 4 5 6 7 8 9
Experiments!!!
9 8 7 6 5 4 3 2 1 0
char *str = "ABCDEFGH";int i = 0;while (*(str + i)) { // <=> *(x+i)!='\0'
cout << str[i++] << "\n";}
Строки – это массивы символов
char *str = "ABCDEFGH";int i = 0;cout << ++str;
char *str = "ABCDEFGH";cout << strlen(str);
28.06.2016
Почувствуйте разницу
© Максименкова О.В., Незнанов А.А., ДПИ и ДАДиИИ
ФКН НИУ ВШЭ14
10 14 -8 23 0 1 43 7 89
int arr[10]; // определён массив из 10 элементов// заполним массив
*(arr++)
arr[1]
arr смещён (содержит новый адрес) на (размер int) байт
и разыменован
К адресу начала массива arr прибавляется 1 * (размер int), затем к
адресу начала массива arr прибавляется 2 * (размер int)
arr++arr arr++
arr *(arr+1) *(arr+2)
arr[2]
28.06.2016
Перебор элементов массива в обратном порядке
© Максименкова О.В., Незнанов А.А., ДПИ и ДАДиИИ
ФКН НИУ ВШЭ15
srand(time(0));int arr[10];for (int i = 0; i < 10; i++) {arr[i] = rand() % 100;cout << arr[i] << " ";
}// используем курсорint* curr = arr + 9;cout << endl;for (int i = 0; i < 10; i++)cout << *curr-- << " ";
C++ pointer arithmetic[http://www.tutorialspoint.com/cplusplus/cpp_pointer_arithmatic.htm]
ArrayPointers_01
Pointers and arrays[http://www.learncpp.com/cpp-tutorial/6-8-pointers-and-arrays/]
28.06.2016
Алгоритмы работы с массивамиПоиск
Сортировки
© Максименкова О.В., Незнанов А.А., ДПИ и ДАДиИИ
ФКН НИУ ВШЭ1628.06.2016
Сортировки
• Сортировка [sorting] –упорядочение набора некоторых объектов по некоторому критерию
• Обычно критерий является формализаций отношения на ключах элементов
• Сортировку можно выполнять путём физического перемещения исходных данных, а можно построить оглавление
© Максименкова О.В., Незнанов А.А., ДПИ и ДАДиИИ
ФКН НИУ ВШЭ17
Sedgewick, Wayne. Algorithms, 4-th ed.
28.06.2016
Сортировка вставками: шаг 1
© Максименкова О.В., Незнанов А.А., ДПИ и ДАДиИИ
ФКН НИУ ВШЭ18
10 7 -34 0 18 -2 23
0 1 2 3 4 5 6
10 10 -34 0 18 -2 23
0 1 2 3 4 5 6
Сдвиг вправо
7 10 -34 0 18 -2 23
0 1 2 3 4 5 6
Размещение
28.06.2016
Сортировка вставками: шаг 2
© Максименкова О.В., Незнанов А.А., ДПИ и ДАДиИИ
ФКН НИУ ВШЭ19
7 10 -34 0 18 -2 23
0 1 2 3 4 5 6
7 7 10 0 18 -2 23
0 1 2 3 4 5 6
Сдвиг вправо
-34 7 10 0 18 -2 23
0 1 2 3 4 5 6
Размещение
28.06.2016
Сортировка вставками: шаг 3
© Максименкова О.В., Незнанов А.А., ДПИ и ДАДиИИ
ФКН НИУ ВШЭ20
-34 7 10 0 18 -2 23
0 1 2 3 4 5 6
Выбор элемента
-34 7 7 10 18 -2 23
0 1 2 3 4 5 6
Сдвиг вправо
-34 0 7 10 18 -2 23
0 1 2 3 4 5 6
Размещение
И так далее…
28.06.2016
Сортировка вставками: реализация
© Максименкова О.В., Незнанов А.А., ДПИ и ДАДиИИ
ФКН НИУ ВШЭ21
void insertionSort(int a[], int n) {for (int i = 1; i < n; i++) {// Левее i-ого элемента массив отсортированint k = a[i]; // Запоминаем, потому что затрем сдвигомint j = i - 1;while (j >= 0 && a[j] > k) {
// Двигаем все элементы, больше ka[j + 1] = a[j];j--;
}a[j + 1] = k; // Ставим k на место
}}
Модифицировать сортировку. Минимальный элемент массива
предварительно установить на первую (с нулевым индексом) позицию. Как
изменится код? Реализуйте модификацию на семинарах.
28.06.2016
Оглавление и его использование
• Оглавление (индексный массив) [index array] – массив курсоров, то есть индексов элементов некоторой последовательности
• Отметим, что часто (например, в теории баз данных) оглавлениеназывают «индексом», что приводит ещё к одной терминологической путанице
• Использование оглавления основано на косвенной адресации исходных данных:
• Вместо 𝐴[𝑖] пишем 𝐴[𝑹𝒆𝑴𝒂𝒑[𝒊]], где 𝑅𝑒𝑀𝑎𝑝[𝑖] – индекс элемента, который должен стоять на месте 𝑖 в соответствии с оглавлением 𝑅𝑒𝑀𝑎𝑝
© Максименкова О.В., Незнанов А.А., ДПИ и ДАДиИИ
ФКН НИУ ВШЭ2228.06.2016
Напоминание: Индексные массивы
© Максименкова О.В., Незнанов А.А., ДПИ и ДАДиИИ
ФКН НИУ ВШЭ23
10 7 -34 0 18 -2 23
0 1 2 3 4 5 6
2 5 3 1 0 4 6
0 1 2 3 4 5 6
Исходный
массив A
Массив индексов (B)
для хранения
сортированного
представления
Как получить значение элемента в массиве А по элементу из массива B?
28.06.2016
Пример: переупорядочение по оглавлению за линейное времяvoid ReMapSource(double* arrSource, int* arrReMap, int Len) {
// AReMapArr – массив курсоров, он будет испорчен…
int i, OldPlace, NewPlace;
double TmpElem;
for (int i = 0; i < Len-1; i++) {
NewPlace = arrReMap[i];
if (NewPlace == i) continue; // Если попалась неподвижная точка
OldPlace = i;
TmpElem = arrSource[i]; //Двигаем цикл перестановки:
do {
arrSource[OldPlace] = arrSource[NewPlace];
OldPlace = NewPlace;
NewPlace = arrReMap[OldPlace];
arrReMap[OldPlace] = OldPlace; //Фиксируем факт перемещения
} while (NewPlace != i);
arrSource[OldPlace] = TmpElem; //Завершаем двигать цикл
};
};© Максименкова О.В., Незнанов А.А., ДПИ и ДАДиИИ
ФКН НИУ ВШЭ24
Здесь полезна трассировка, сделаем её на семинарах
28.06.2016
Пример: бинарный поиск по совпадениюint binarySearch(int* arr, int arrLen, int seekingValue) {
int index = -1; // индекс найденного элемента
for (int i = 0, j = arrLen - 1, k = j % 2; i <= j;
k = (i + j) / 2) {
if (arr[k] == seekingValue) {
index = k; break; // нашли - прерываем цикл
} else {
if (seekingValue > arr[k]) i = k + 1;
else j = k - 1;
}
}
return index;
}
© Максименкова О.В., Незнанов А.А., ДПИ и ДАДиИИ
ФКН НИУ ВШЭ2528.06.2016
Массивы и функции
© Максименкова О.В., Незнанов А.А., ДПИ и ДАДиИИ
ФКН НИУ ВШЭ26
#include <iostream>using namespace std;void changeArray(int[]);int main() {
int ar[] = { 1,2,3,4 };cout << *ar << *ar + 1 << *ar + 2 << *ar + 3;cout << "\n";changeArray(ar);cout << ar[0] << ar[1] << ar[2] << ar[3];cout << "\n";cout << *ar << *ar + 1 << *ar + 2 << *ar + 3;return 0;
}void changeArray(int ar[]) {
ar[0] = ar[3];}
Передача массива в функцию и немного подозрительного кода
28.06.2016
Модификация с указателем
© Максименкова О.В., Незнанов А.А., ДПИ и ДАДиИИ
ФКН НИУ ВШЭ27
#include <iostream>using namespace std;void changeArray(int*);int main() {
int ar[] = { 1,2,3,4 };cout << *ar << *(ar + 1) << *(ar + 2) << *(ar + 3);cout << "\n";changeArray(ar);cout << *ar << *(ar + 1) << *(ar + 2) << *(ar + 3);return 0;
}void changeArray(int* ar) {
ar[0] = ar[3];}
28.06.2016
Многомерные массивы
© Максименкова О.В., Незнанов А.А., ДПИ и ДАДиИИ
ФКН НИУ ВШЭ28
тип имя_массива[К1][K2]...[KN];
массив, элементами которого служат массивы
#include <iostream>using namespace std;
const int N = 3;const int M = 5;const int K = 7;int main() {
int multiArr[N][M][K]; // трёхмерный массивreturn 0;}
К всему прочему, мы можем описать массив указателей
28.06.2016
© Максименкова О.В., Незнанов А.А., ДПИ и ДАДиИИ
ФКН НИУ ВШЭ29
#include <iostream>using namespace std;
const int N = 3;const int M = 5;const int K = 7;int main() {
int multiArr[N][M][K] = {1, 2, 3, 4, 5, 6, 7, 8};for (int i = 0; i < N; i++)
for (int j = 0; j < M; j++)for (int k = 0; k < K; k++)
cout << multiArr[i][j][k] << " ";return 0;
}
int multiArr[N][M][K] = { {1,2,3},{4,5},{6,7,8} };
Что изменится?
28.06.2016
Многомерные массивы и указатели
© Максименкова О.В., Незнанов А.А., ДПИ и ДАДиИИ
ФКН НИУ ВШЭ30
const int N = 2;const int M = 2;const int K = 2;
int multiArr[N][M][K] = { {1,2},{3,4},{5,6}};
int multiArr[N][M][K] = { {1,2},{3,4} };
#include <iostream>using namespace std;
const int N = 2;const int M = 2;
int main() {int multiArr[N][M] = { {1,2},{3,4} };cout << *multiArr << "\n";cout << **multiArr << "\n";return 0;
}
*(*(multiArr + 1)) + 1 **multiArr + 1
28.06.2016
Многомерные массивы с C++: проблемы
• Очень серьёзная проблема! В данном случае гибкость языка с кучей «синтаксического сахара» очень мешает школьникам…
• Многие источники, даже ориентированные на обучение, дают очень странные советы (называется – «Плодим уродцев»)
• Например: Two Dimensional Array (http://www.cppforschool.com/tutorial/array2.html)
1. void print(int A[][3], int N, int M) {
2. for (R = 0; R < N; R++)
3. for (C = 0; C < M; C++)
4. cout << A[R][C];
5. }
© Максименкова О.В., Незнанов А.А., ДПИ и ДАДиИИ
ФКН НИУ ВШЭ31
Константа!!! А без неё не получалось…
Кто ответит –
почему?
28.06.2016
Многомерные массивы в С++: решения
• Выхода всего два:
1. Массив указателей на массивы
2. Класс-обёртка для одномерного массива:
• При этом создание класса может сделать работу с ним практически неотличимой от «обычного» массива
© Максименкова О.В., Незнанов А.А., ДПИ и ДАДиИИ
ФКН НИУ ВШЭ3228.06.2016
Пример: динамический двумерный массив в С++11
• const auto RowCount = 2;
• const auto ColCount = 3;
• auto array = new double[RowCount][ColCount]; // Выделяем память
• // Заполняем массив
• for (int r = 0; r < RowCount; r++) {
• for (int c = 0; c < ColCount; c++) {
• array[r][c] = (r + 1)*(c + 1);
• };
• };
• // Распечатываем массив
• for (int r = 0; r < RowCount; r++) {
• for (int c = 0; c < ColCount; c++) {
• cout << array[r][c] << " ";
• };
• cout << endl;
• }
• cout << endl;
• delete[] array; // Удаляем массив
© Максименкова О.В., Незнанов А.А., ДПИ и ДАДиИИ
ФКН НИУ ВШЭ33
А вот и часть ответа
на предыдущий вопрос
28.06.2016
Пример: шаблонный класс двумерного массива – вполне рабочая заготовка
1. template <typename T>
2. class MyMatrix {
3. size_t _rows;
4. size_t _columns;
5. std::unique_ptr<T[]> data; // Данные как одномерный массив
6. public:
7. MyMatrix(size_t rows, size_t columns)
8. : _rows{ rows }, _columns{ columns }, data{ nullptr } {
9. data = std::make_unique<T[]>(rows * columns);
10. } // Конструктор
11. size_t rows() const {return _rows;} // Число строк
12. size_t columns() const {return _columns;} // Число столбцов
13. T * operator[](size_t row) {
14. return row * _columns + data.get();
15. } // Индексатор
16. };
© Максименкова О.В., Незнанов А.А., ДПИ и ДАДиИИ
ФКН НИУ ВШЭ3428.06.2016
Обсуждение примеров с двумерными массивами
• Класс из предыдущего примера можно использовать для замены обычного двумерного массива, только теперь можно делать размеры не константами!
• Было:
1. const auto RowCount = 2;
2. const auto ColCount = 3;
3. auto array = new double[RowCount][ColCount];
• Стало:
1. int RowCount = 3;
2. int ColCount = 4;
3. MyMatrix<double> MM(RowCount, ColCount);
© Максименкова О.В., Незнанов А.А., ДПИ и ДАДиИИ
ФКН НИУ ВШЭ3528.06.2016
Решаем задачи. Массивы
1. В целочисленном массиве А определить и вывести на экран «расстояние» между первым вхождением минимального и первым вхождением максимального элементов. Расстояние между элементами A[i] и A[j] – количество элементов между ними.
2. По массиву целочисленному массиву A сформировать вещественный массив B, содержащий абсолютные значения отклонений элементов массива A от среднего значения элементов массива.
3. Выполнить некольцевой сдвиг всех элементов массива на K позиций вправо.
4. Из целочисленного массива удалить все отрицательные элементы со сдвигом влево.
5. Выполнить кольцевой сдвиг элементов вещественного массива на Kпозиций влево.
6. По целочисленному массиву A сформировать индексный массив B для хранения сортированного представления массива A.
© Максименкова О.В., Незнанов А.А., ДПИ и ДАДиИИ
ФКН НИУ ВШЭ3628.06.2016
© Максименкова О.В., Незнанов А.А., ДПИ и ДАДиИИ
ФКН НИУ ВШЭ37
Спасибо за внимание!
Максименкова Ольга Вениаминовна
Старший преподаватель Департамента программной инженерии, ФКН
E-mail: [email protected]
Blog: Stop To Scale (http://stoptoscale.blogspot.ru)
28.06.2016