Введите первую строку: Строка 1.
Введите вторую строку: Строка 2.
До копирования строки были не одинаковыми.
После копирования строки стали одинаковыми.
После конкатенации первая строка равна: Строка 2.Строка 2.
6. Сводка результатов
Набор однотипных переменных можно представить в виде массива. Массивы
можно передавать в качестве параметров внутрь функций. В лекции описан простой
алгоритм сортировки массивов методом выбора наименьшего элемента. Кратко рас-
смотрены приемы работы с двумерными массивами.
Символьные строки в Си++ хранятся в виде символьных массивов, последний
символ в которых является служебным нуль-символом. В стандартной библиотеке
Си++ есть специальные функции для обработки символьных строк.
7. Упражнения
Упражнение 1
Напишите библиотеку функций для работы с целочисленными массивами.
Прототипы _______функций поместите в заголовочный файл "IntegerArray.h", а опреде-
ления –в файл реализации "IntegerArray.cpp". Библиотека должна содержать
следующие функции:
•"input_array(a,n)" для ввода с клавиатуры первых n значений массива a.
•"display_array(a,n)" для вывода на экран первых n значений массива a.
•"copy_array(a1,a2,n)" для копирования первых n элементов массива a2 в соответ-
ствующие элементы массива a1.
•"standard_deviation(a,n)", которая вычисляет и возвращает среднеквадратическое
отклонение первых n элементов массива a. (Для реализации этой функции может
пригодиться функция "average(a,n)" из программы 2.1.) Формула для вычисления
среднеквадратического отклонения дана в упражнении 3 из лекции 3.
Проверьте разработанные функции с помощью подходящей тестовой программы.
74
Упражнение 2
Из функции "selection_sort(...)", рассматривавшейся в лекции, сделайте
функцию сортировки символьной строки "string_sort(...)". У этой функции один
параметр –строка, которая должны быть отсортирована в алфавитном порядке (точ-
нее, по возрастанию ASCII-кодов, при этом все прописные буквы будут помещены до
строчных). Позиция завершающего нуль-символа при сортировке не меняется. Про-
верьте работу функции с помощью тестовой программы, которая должна выводить на
экран сообщения:
Введите строку: Вася Иванов
Отсортированная строка равна: ВИааввнося
Упражнение 3
Напишите функцию "no_repetitions(...)", которая удаляет из строки все по-
вторяющиеся символы. Напишите тестовую программу для проверки этой функции,
которая воспроизводит следующий диалог с пользователем:
Введите строку: Эта строка содержит повторяющиеся символы
Строка без повторяющихся символов равна: Эта срокдежипвяющмлы
Подсказка: Как и многие задачи программирования, эту задачу легче решить с по-
мощью процедурной абстракции.
Упражнение 4
С использованием двумерных массивов напишите функцию (и соответствую-
щую тестовую программу) для умножения целочисленной матрицы размером mxn на
матрицу размером nxr. В тестовой программе для задания значений m, n и r заведите
глобальные константы. Ниже приведен пример сообщений, выдаваемых программой:
Введите первую матрицу (размер 2x2):
Введите 2 значения для 1-й строки (через пробелы): 3 4
Введите 2 значения для 2-й строки (через пробелы): 5 7
Введите вторую матрицу (размер 2x2):
Введите 2 значения для 1-й строки (через пробелы): 1 1
Введите 2 значения для 2-й строки (через пробелы): 2 2
3 4
5 7
УМНОЖИТЬ
1 1
2 2
РАВНО
11 11
19 19
75
ЛЕКЦИЯ 7. Указатели
1. Назначение указателей
Язык Си++ унаследовал от языка Си мощные средства для работы с оператив-
ной памятью: динамическое выделение и освобождение блоков памяти, доступ к от-
дельным ячейкам памяти по их адресам. Эти возможности делают язык Си++, как и
Си, удобным для разработки системного программного обеспечения и прикладных
программ, в которых применяются динамические структуры данных (т.е. такие, раз-
мер которых не известен на этапе компиляции и может меняться во время выполне-
ния программы).
Во всех программах из предыдущих лекций переменные объявлялись так, что
компилятор резервировал для каждой из них некоторое количество памяти (в соот-
ветствии с типом данных) еще на этапе компиляции. В начале выполнения блока опе-
раторов, внутри которого объявлена переменная, автоматически выполняется выде-
ление памяти для этой переменной, а при выходе из блока –освобождение памяти.
В данной лекции подробно рассматривается понятие указателя, –средства, ко-
торое дает программисту наиболее гибкий способ контроля операций выделе-
ния/освобождения памяти во время выполнения программ.
1.1 Объявление указателей
Указателем называется адрес переменной в оперативной памяти. Переменная
указательного типа (часто она называется переменная-указатель или просто указа-
тель) –это переменная, размер которой достаточен для хранения адреса оперативной
памяти. Переменные-указатели объявляются с помощью символа "*", который добав-
ляется после названия обычного типа данных. Например, оператор описания (его
можно прочитать как "указатель на целочисленную переменную"):
int* number_ptr;
объявляет переменную-указатель "number_ptr", которая может хранить адреса пере-
менных типа "int".
Если в программе используется много однотипных указателей, то для их объ-
явления можно ввести новый тип. Это делается с помощью оператора описания ново-
го типа "typedef". Например, если записать оператор:
typedef int* IntPtrType;
то в программе можно будет объявлять сразу несколько переменных-указателей, не
применяя для каждой из них символ "*":
IntPtrType number_ptr1, number_ptr2, number_ptr3;
1.2 Применение символов "*" и "&" в операторах присваивания значений указателям
В операторах присваивания допускается совместное использование обычных и
указательных переменных одного типа (например, "int"). Для получения значения
переменной по ее указателю предназначена операция разыменования указателя "*". У
нее есть обратная операция –операция взятия адреса "&". Упрощенно, операция "*"
означает "получить значение переменной, расположенной по этому адресу", а опера-
ция "&" – "получить адрес этой переменной". Для пояснения смысла этих операций
далее приводится программа 1.1.
76
#include <iostream.h>
typedef int* IntPtrType;
int main()
{
IntPtrType ptr_a, ptr_b;
int num_c = 4, num_d = 7;
ptr_a = &num_c; /* СТРОКА 10 */
ptr_b = ptr_a; /* СТРОКА 11 */
cout << *ptr_a << " " << *ptr_b << "\n";
ptr_b = &num_d; /* СТРОКА 13 */
cout << *ptr_a << " " << *ptr_b << "\n";
*ptr_a = *ptr_b; /* СТРОКА 15 */
cout << *ptr_a << " " << *ptr_b << "\n";
cout << num_c << " " << *&*&*&num_c << "\n";
return 0;
}
Программа 1.1.
Программа 1.1 выдает на экран следующие сообщения:
4 4
4 7
7 7
7 7
Графически состояние программы 1.1 после выполнения операторов присваи-
вания в 10-11-й строках, 15-й и 19-й показано, соответственно, на рис. 1, 2 и 3.
Рис. 1.. Состояние про-
граммы 1.1 после выпол-
нения 10-й и 11-й строк.
Рис. 2.. Состояние про-
граммы 1.1 после выпол-
нения 13-й строки.
Рис. 3.. Состояние про-
граммы 1.1 после выпол-
нения 15-й строки.
Операции "*" и "&", в некотором смысле, являются взаимно обратными, так что
выражение "*&*&*&num_c" означает то же самое, что и "num_c".
1.3 Операторы "new" и "delete". Константа "NULL"
Рассмотрим оператор присваивания в 10-й строке программы 1.1:
ptr_a = &num_c;
Можно считать, что после выполнения этого оператора у переменной "num_c"