- •Аннотация
- •Предисловие
- •Г л а в а 1. Введение в указатели
- •§1. Понятие указателя. Операции разыменования и разадресации
- •§2. Инициализация и присваивание указателей
- •§3. Передача параметров функций с помощью указателей.
- •§4. Распределение динамической памяти.
- •4.1. Операция new
- •4.2. Операция delete
- •Упражнения, тесты.
- •Указатели и массивы
- •§1. Связь указателей и массивов
- •1.1. Указатели и одномерные массивы
- •1.2. Указатели и матрицы
- •Int MyFun (int *X, int n)
- •§3. Операции над указателями при работе с массивами.
- •3.1. Арифметические операции
- •3.2. Операции сравнения.
- •§4. Использование операций над указателями при работе с одномерными массивами
- •4.1. Использование индексов
- •4.2. Указатель в качестве параметра цикла
- •4.3. Использование указателя и индекса
- •§5. Строки.
- •5.1. Общая характеристика строк.
- •5.2. Примеры алгоритмов работы со строками.
- •5.3. Анализ строковых функций.
- •§6. Использование операций над указателями при работе со статической матрицей.
- •Упражнения, тесты.
- •Массивы указателей
- •§1. Статический массив указателей
- •§2. Частично динамическая матрица.
- •Int arr_of_size[n];
- •§3. Статический массив строк
- •§4. Динамический массив указателей
- •4.1. Указатель на указатель
- •4.2. Динамические “матрицы”.
- •Int *arr_of_size;
- •4.3. Передача матрицы в функцию
- •Int a[10]; FunArr1(a, 10,…);
- •Упражнения, тесты.
- •Задачи второго среднего уровня.
- •Структуры и другие типы, определяемые пользователем
- •§1. Структуры
- •Объявление структуры
- •1.2. Работа со структурой.
- •1.3. Вложенные структуры и статические массивы в структурах
- •1.4. Статический массив структур
- •§2. Cтруктуры и указатели
- •2.1. Указатели в структуре.
- •2.2. Указатели на структуру
- •2.3. Динамический массив структур
- •2.4. Ссылка на структуру.
- •2.5. Указатели и вложенные структуры
- •§3. Cтруктуры и функции
- •3.1. Передача полей структуры в функцию.
- •Void MyFun1 (int X, float &y, int *u1, float *u2, char *s);
- •3.2. Передача всей структуры в функцию
- •Void Fun1 (tst s,…);
- •Void Fun2 (tst & s,…);
- •Void Fun3 (tst* s,…);
- •§4. Cтруктуры и классы.
- •§5. Объединения.
- •Представление вещественных чисел в памяти компьютера.
- •§6. Поля битов (битовые поля)
- •Ввод в ы в о д
- •Symbol Code16 Code10 Code2
- •§7. Перечисления
- •Какие из строк (//1 – //9) правильные?
Г л а в а 1. Введение в указатели
Одной из причин, почему язык С++ объективно более сложный по сравнению с другими языками (например, Pascal), является широкое использование переменных-указателей.
§1. Понятие указателя. Операции разыменования и разадресации
Указатель — это специальная переменная, которая хранит адрес другой переменной. Указатель объявляется следующим образом:
тип * переменная;
где тип — любой допустимый как простой, так и составной базовый тип указателя. Например, пусть объявлена обычная переменная
int t.
Объявление и инициализация
int *p= &t;
означают следующее. В переменной p будет храниться не обрабатываемое программой целое число (оценка студента, количество выпущенной продукции и т. п.), а адрес ячейки, в которой будет находиться информация указанного типа (целое число). Под адресом будем понимать номер первого байта выделенного для переменной участка оперативной памяти в шестнадцатеричной системе счисления. Для переменных, не являющихся указателями, без дополнительного объявления адрес также запоминается системой, и его можно получить с помощью операции & (разадресации), например, &t. Эта унарная операция, которую иногда называют “взятие адреса”, ничего не делает со значением переменной t.
До первого использования переменная – указатель обязательно должна быть проинициализирована. До тех пор, пока не определим значение указателя, он относится к чему-то случайному в памяти, и его использование может привести к непредсказуемым результатам. Один из способов показан выше и означает, что в переменную p помещается адрес ячейки t. Важно понять, что приведённая выше запись равносильна
int *p; и p=&t ; ( а не *p=&t;)
В этом заключается одна из трудностей начального этапа изучения указателей. Эта тема усложняется ещё и тем, что такой же символ “&” используется при объявлении переменной ссылочного типа. Здесь этот символ определяет операцию взятия адреса для переменной и никакого отношения к ссылочному типу не имеет.
Заметим, что расстановка пробелов при объявлении указателей свободная. Наряду с приведенными выше допустимы также следующие записи:
int * p= & t; int* p= &t;.
Предпочтение следовало бы отдать последней записи, так как она подчёркивает, что объявляется переменная p, а не *p. Из такой записи легче понять смысл указателя. Она показывает, что типом является int*, а не int. Из синтаксических правил необходимо обратить также внимание на следующее. Если в одной строке объявляется несколько указателей, то символ “*” надо писать перед каждой переменной: float* q1, *q2;
Содержимое ячейки, адрес которой находится в p, в тексте программы обозначается с помощью операции разыменование. Для неё используется тот же символ “*”, что и при объявлении переменной-указателя. Эта унарная операция возвращает значение переменной, находящейся по указанному адресу. Поэтому *p — это обрабатываемое программой целое число, которое находится в ячейке, адрес которой в переменной-указателе p. С учётом инициализации (p=&t) *p и t — это одно и тоже значение. Поэтому если с помощью cin>>t введём, например, число 2 и выполним *p*=5, что равносильно *p=*p*5, то изменится и величина t, хотя, казалось бы, не было явного её изменения. Поэтому оператор cout << t; выведет число 10 (2*5). И, наоборот, изменив t (например, t++;) этим самым мы изменим и значение *p. С помощью cout<<(*p) выведем 11.
Сказанное выше будем обозначать следующим образом:
p (или &t) *p (или t)
В “левом прямоугольнике” (ячейке памяти) находится адрес, а в ячейке “справа” — обрабатываемое целое число. Этот рисунок объясняет термин “указатель”.
Заметим, что рассматриваемые здесь операции “&” и ”*” являются унарными и имеют более высокий приоритет по сравнению с аналогичными бинарными операциями “битовое и” и арифметическое умножение.
Для *p определены те же операции, что и для переменной указанного типа, у нас для целых чисел. Поэтому допустимы, например, следующие операторы:
а) cin>>(*p); б) int r; r=*p*2; в) if (*p%2) …; г)cout<<(*p);
Можно также выводить значение переменной-указателя. После выполнения cout<<p; будет выведен адрес в шестнадцатеричной системе счисления. При этом он не обязательно будет одинаковым при повторном выполнении одной и той же программы.