1. Модуль 2: Составные типы данных и работа с динамической
памятью.
Темы лекции: Массивы и указатели.
Практическое задание: Алгоритмы сортировки массивов.
Тренер: Игорь Шкулипа, к.т.н.
C++ Базовый. Занятие 5
2. http://www.slideshare.net/IgorShkulipa 2
Массив
Массив - это совокупность данных одного типа, к которым обращаются с
помощью общего имени.
Доступ к отдельному элементу массива может осуществляться с помощью
индекса.
В С++ все массивы состоят из соприкасающихся участков памяти.
Наименьший адрес соответствует первому элементу. Наибольший
адрес соответствует последнему элементу.
Массивы могут иметь одну или несколько размерностей.
Каждый элемент массива может использоваться, как отдельная
переменная
3. http://www.slideshare.net/IgorShkulipa 3
Одномерный массив
Стандартный вид объявления одномерного массива следующий:
<тип> <имя_переменной> [<размер>];
В С++ массивы должны определяться однозначно, чтобы компилятор мог
выделить для них место в памяти.
Здесь <тип> объявляет базовый тип массива и является типом каждого
элемента массива.
Параметр <размер> определяет, сколько элементов содержит массив.
В одномерном массиве полный размер массива в байтах вычисляется
следующим образом:
<общее число байт> = sizeof (<тип>) *<количество элементов>
У всех массивов первый элемент имеет индекс 0. Поэтому
char charArray[10];
объявляет массив символов из 10 элементов, причем эти элементы
адресуются индексом от 0 до 9.
4. http://www.slideshare.net/IgorShkulipa 4
Объявление и определение массивов
int array1[];
Объявлен массив типа int, имя массива - array1
int array2[10];
Определен массив типа int, размером 10 элементов
int array3[<константное выражение>];
Определен массив типа int, размером <константное выражение>
элементов
int array4[3]={1,2,3};
В этом определении массива важно, чтобы количество элементов в
инициализаторе массива не превышало значение константного
выражения в описателе массива.
int array5[]={1,2,3};
Количество элементов массива становится известным компилятору при
анализе инициализатора.
5. http://www.slideshare.net/IgorShkulipa 5
Частичная инициализация массивов
int array6[3]={1,2};
Возможна частичная инициализация массива. При этом значения
получают первые элементы массива. Значение последнего элемента
массива в общем случае не определено (обычно равно 0).
int array7[3]={1,2,};
Последняя запятая предупреждает о факте частичной инициализации
массива.
6. http://www.slideshare.net/IgorShkulipa 6
Доступ к элементам массива
int array[] = { 0, 1, 2, 4, 8, 16, 32, 64, 128, 256 };
array[0]=0; // доступ к нулевому элементу
array[1]=1; // доступ к первому элементу
array[2]=2; // доступ ко второму элементу
...
array[9]=256; // доступ к девятому элементу
Следующее выражение меняет местами 6 и 8 элемент массива:
array[6]=array[6]+array[8];
array[8]=array[6]-array[8];
array[6]=array[6]-array[8];
7. http://www.slideshare.net/IgorShkulipa 7
Многомерные массивы
В С++ есть возможность использовать многомерные массивы, при
объявлении которых необходимо указать размер каждого измерения в
отдельных квадратных скобках.
int array1[4][3];
Многомерные массивы тоже могут быть инициализированы:
int array2[4][3] = {{0,1,2},{3,4,5},{6,7,8},{9,10,11}};
Внутренние фигурные скобки, разбивающие список значений на строки,
необязательны и используются, как правило, для удобства чтения
кода.
int array3[4][3] = { 0,1,2,3,4,5,6,7,8,9,10,11 };
Инициализация массива array3 полностью эквивалентна array2
8. http://www.slideshare.net/IgorShkulipa 8
Многомерные массивы
int array4[4][3] = {{0},{3},{6},{9}};
Это определение инициализирует только первые элементы каждой строки.
Оставшиеся элементы будут равны нулю.
int array5[4][3] = {0,3,6,9};
Если же опустить внутренние фигурные скобки, то три элемента первой строки и
первый элемент второй получат указанное значение, а остальные будут
инициализированы 0.
При обращении к элементам многомерного массива необходимо использовать
индексы для каждого измерения (они заключаются в квадратные скобки). Так
выглядит инициализация двумерного массива с помощью вложенных циклов:
int main()
{
const int iRows = 4;
const int iCols = 3;
int array[iRows][iCols];
for ( int i = 0; i < iRows; i++ )
for ( int j = 0; j < iCols; j++ )
array[i][j] = i + j;
}
9. http://www.slideshare.net/IgorShkulipa 9
Динамическая память
Память, выделяемая в С++ функциями динамического распределения
данных, находится в так называемой динамически распределяемой
области памяти (синонимы: heap, куча, динамическая память).
Динамическая память — это свободная область памяти, не
используемая программой, операционной системой или другими
программами. Размер динамически распределяемой области памяти
заранее неизвестен, но, как правило, ограничен объемом оперативной
памяти вычислительной системы.
10. http://www.slideshare.net/IgorShkulipa 10
Указатели
Указатели - это практически то же самое, что и переменные. Они
отличаются тем, что вместо того, чтобы содержать данные, они
содержат адрес памяти, где данная информация может быть
найдена.
Для объявления указателя существует следующая конструкция:
<тип> * <имя переменной>
Например:
int* pointer1;
int* pointer2;
Указатели имеют тип, который определяет размер ячейки памяти и тип
хранимого в ней значения.
11. http://www.slideshare.net/IgorShkulipa 11
Разыменование и взятие адреса
Основной операцией при работе с указателями является получение
доступа к значению, адрес которого хранится в указателе.
Например:
int *intPointer, intVariable;
*intPointer = 5;
intVariable = *intPointer;
Выражение *intPointer имеет такой же смысл, как имя целой
переменной. Операция * называется разыменованием.
Действие, обратное к разыменованию, позволяет получить адрес
переменной по ее имени. Эта операция называется взятие адреса.
Например:
intPointer = &intVariable;
12. http://www.slideshare.net/IgorShkulipa 12
Выделение памяти под указатель (malloc)
Основу системы динамического распределения в С составляют функции
malloc() и free(). Эти функции работают совместно. Функция malloc()
выделяет память, а free() — освобождает ее. Это значит, что при каждом
запросе функция malloc() выделяет требуемый участок свободной
памяти, a free() освобождает его, то есть возвращает системе. В
программу, использующую эти функции, должен быть включен
заголовочный файл <stdlib.h>.
void *malloc(<количество_байтов>);
Например:
char *p;
// выделение 1000 байт памяти
p = (char*)malloc(1000);
// выделение 50*sizeof(int), т.е. 200 байт памяти
p = (char*)malloc(50*sizeof(int));
13. http://www.slideshare.net/IgorShkulipa 13
Освобождение памяти указателя (free)
Функция free() возвращает системе участок памяти, выделенный ранее
с помощью функции malloc(). Иными словами, она освобождает
участок памяти, который может быть вновь использован функцией
malloc().
Функция free() имеет следующий прототип:
void free(void *p)
Например:
char *p;
// выделение 1000 байт памяти
p = malloc(1000);
// освобождение памяти
free (p);
// выделение 50*sizeof(int), т.е. 200 байт памяти
p = malloc(50*sizeof(int));
// освобождение памяти
free (p);
14. http://www.slideshare.net/IgorShkulipa 14
Выделение памяти под указатель (new)
Выделение памяти объекту может производиться с помощью оператора new,
возвращающего указатель на вновь созданный объект того типа, который
был ему задан.
int *p = new int;
Размещает объект типа int в памяти и инициализирует указатель p адресом
этого объекта. Сам объект в таком случае не инициализируется.
Чтобы инициализировать объект в памяти, используется следующая
конструкция.
int *p = new int(10);
Можно динамически выделить память под массив:
int *p = new int[10];
Такая инструкция размещает в памяти массив из десяти элементов типа int.
15. http://www.slideshare.net/IgorShkulipa 15
Освобождение памяти указателя (delete)
После использования объекта, созданного с помощью оператора new, мы
должны явно освободить память, применив оператор delete к
указателю на этот объект.
(!) Попытка применить оператор delete к указателю, не содержащему
адрес объекта, полученного описанным способом, вызовет ошибку
времени выполнения.
delete p;
Освобождает память, на которую указывает указатель p.
delete[] p;
Освобождает память, отведенную под массив p.
16. http://www.slideshare.net/IgorShkulipa 16
Связь массивов и указателей
Если мы имеем определение массива:
int array[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
То использование идентификатора массива в программе эквивалентно
указанию адреса его первого элемента:
array == &array[0];
*array == array[0];
Соответственно:
&array[1] == array+1;
array[1]==*(array+1)
* - операция разыменования указателя, то есть значение ячейки
памяти, на которую он указывает
17. http://www.slideshare.net/IgorShkulipa 17
Операции с указателями
Над указателями можно выполнять унарные операции: инкремент и
декремент. При выполнении операций ++ и -- значение указателя
увеличивается или уменьшается на длину типа, на который ссылается
используемый указатель.
int *pointer;
int array[10];
pointer=&array[5];
pointer++; //адрес элемента a[6]
pointer--; //адрес элемента a[5]
В бинарных операциях сложения и вычитания могут участвовать
указатель и величина типа int. При этом результатом операции будет
указатель на исходный тип, а его значение будет на указанное число
элементов больше или меньше исходного.
int *pointer1, *pointer2;
int array[10];
pointer1=array+4; //адрес элемента a[4]
pointer2=pointer1-2; //адрес элемента a[2]
18. http://www.slideshare.net/IgorShkulipa 18
Операции с указателями
В операции вычитания могут участвовать два указателя на один и тот же
тип. Результат такой операции имеет тип int и равен числу элементов
исходного типа между уменьшаемым и вычитаемым, причем если
первый адрес младше, то результат имеет отрицательное значение.
int *pointer1, *pointer2, array[10], i;
pointer1=array+4;
pointer2=array+9;
i=pointer1-pointer2; // i==5
i=pointer2-pointer1; // i==-5
Значения двух указателей на одинаковые типы можно сравнивать в
операциях == != < <= > >= при этом значения указателей
рассматриваются просто как целые числа.
int *pointer1, *pointer2, array[10], i;
pointer1=array+4;
pointer2=array+9;
if (pointer1 > pointer2) array[5]=10;
19. http://www.slideshare.net/IgorShkulipa 19
Передача массивов в функции
Когда массив используется в качестве аргумента функции, передается
только адрес массива, а не копия всего массива. При вызове
функции с именем массива в функцию передается указатель на
первый элемент массива. Параметр должен иметь тип, совместимый с
указателем. Имеется три способа объявления параметра,
предназначенного для получения указателя на массив.
void arrayFunction(int array[10]);
void arrayFunction(int array[]);
void arrayFunction(int *array);
Для многомерных массивов:
void arrayFunction(int array[10][10]);
void arrayFunction(int array[][10]);
void arrayFunction(int **array);
20. http://www.slideshare.net/IgorShkulipa 20
Лабораторная работа №5
Создать массив случайных чисел. Отсортировать массив в порядке
возрастания/убывания, используя алгоритм:
⚫ Быстрой сортировки (1, 7, 13)
⚫ Сортировки Шелла (2, 8, 14)
⚫ Сортировки простыми вставками (3, 9, 15)
⚫ Пирамидальной сортировки (4, 10)
⚫ Сортировки “шейкерным” методом (5, 11)
⚫ Поразрядной сортировки (6, 12)
Реализовать параметризацию функции сортировки по возрастанию и по
убыванию элементов.
Оформить методы сортировки в виде отдельного модуля, размещенного в
отдельном подключаемом файле.