- •Введение
- •Структура программы на языке Си
- •Директивы препроцессора
- •Константы
- •Переменные
- •Арифметические выражения
- •Операция присваивания
- •Ввод информации с клавиатуры и вывод на экран
- •Логические выражения
- •Операторы управления
- •Оператор условного перехода if
- •Оператор выбора варианта
- •Перечисляемый тип данных
- •Циклы
- •Оператор цикла while
- •Оператор цикла do-while
- •Оператор цикла for
- •Локальные и глобальные переменные
- •Переменные с индексами (массивы)
- •Примеры обработки одномерных массивов
- •Многомерные массивы
- •Массивы и указатели
- •Арифметические и логические операции с указателями
- •Обработка массивов с помощью указателей
- •Обработка массивов переменной размерности
- •Обработка матриц переменной размерности
- •Обработка текстовой информации
- •Стандартные строки языка С++
- •Пример 1. Определение длины строки.
- •Пример 2. Копирование одной строки в другую
- •Пример 3. Исключение из строки символа.
- •Пример 4. Вставка в строку символа
- •Пример 5. Проверка правильности расстановки скобок.
- •Строки типа string
- •Массивы указателей
- •Указатели на функции
- •Сводная таблица форм объявления указателей
- •Преобразование типов
- •Ссылки
- •Структуры
- •Объединения
- •Файлы
- •Чтение файла в матрицу
- •Чтение файла в структуру
- •Функции для обработки произвольных полей структур
- •Связные списки
- •Контейнерные классы
- •Стеки
- •Рекурсия
- •Вопросы для самопроверки
- •Литература
84
Арифметические и логические операции с указателями
Любой объект программы имеет строго определенную длину, определяемую типом объекта. Если указатель связан с объектом, то компилятору эта длина известна. Поэтому, когда указатель связан с массивом, увеличение значения указателя на 1 настраивает указатель на следующий элемент этого массива (а не на следующий байт памяти, если элемент имеет длину более одного байта). Таким образом, выполняя арифметические операции с указателем можно его настраивать на другие элементы массива, аналогично тому, как это выполняется с помощью индексов.
На операции, которые можно выполнять с указателями, налагаются ограничения.
1.Скалярные указатели можно приравнивать друг другу, если они связаны с элементами данных одного и того же типа. Пусть
int *p, *q;
double *s, *t;
тогда допускается операция p = q; (или q = p;),
но не допускается операция p = s; (или p = t;).
2.Указатели разных видов (рабочие и указатели-константы) можно использовать в операциях присваивания, если они связаны с однотипными данными, но с левой стороны от знака равенства может быть только рабочий указатель.
Указатели-константы не могут записываться с левой стороны от знака равенства, поскольку они являются константами, и после такого присваивания массив, с которым связан указатель, будет утерян в памяти программы. Потребность в этом возникает при необходимости совмещения двух или более массивов или разнотипных данных. В этом случае можно воспользоваться преобразованием типа reinterpret_cast (см. параграф «Преобразование типов»).
85
3.Указатели на данные типа void можно преобразовывать для обращения к элементам данных любых типов. Например, указатель на тип данных void преобразуется в указатель на тип данных double (эта операция называется явным приведением типа), с помощью указанной ниже формы записи
void *p; double *q;
...
q = (double*)p;
4.Указатель любого типа можно суммировать с целочисленными константами или с арифметическими выражениями, которые в результате вычисления дают целое число. Такое суммирование имеет смысл только в том случае, если указатель связан с массивом, структурой или классом. Например, пусть имеется объявление
int *ptr, j, x[20];
Тогда выражение ptr++ после вычисления может быть и будет случайно указывать на переменную j, но если записать
ptr = x;
то выражение ptr++ после вычисления однозначно будет указывать на элемент x[1]. Аналогично, после выполнения действий
ptr = x; j = 3;
ptr = ptr + 2 * j + 1;
указатель ptr будет указывать на элемент x[7]. Если при вычислении выражения с указателем получится значение адреса за пределом массива, то действия программы непредсказуемы. Нарушение границ массивов в языке C++ не проверяется.
5.Если указатели связаны с одним и тем же объектом, то из одного указателя можно вычитать другой (для массивов эта разность равна количеству элементов, размещенных между ними).
6.Указатели, связанные с одним и тем же массивом (один из этих указателей может быть только скалярным), можно сравнивать на
86
"больше" или "меньше", а также на "равенство" или "неравенство". Указатели можно также сравнивать со специальным символом NULL, который обозначает пустой указатель (не связанный с конкретным адресом). Такое значение обычно указатель получает, если при запросе память не была выделена.
7.Никакие операции с указателями, кроме перечисленных, не допускаются, т.е. не разрешены умножение (и деление) указателя на указатель или умножение указателя на число, а также суммирование с числами типа double, поскольку при этом получаются адреса памяти, не имеющие отношения к программе.
Обработка массивов с помощью указателей
Спомощью указателей можно обрабатывать массивы. При этом возможны два варианта:
Использование альтернативной формы записи индексов массива (например, запись *(a + i) вместо a[i]). Кроме потери наглядности этот способ никаких преимуществ не имеет, поэтому его следует назвать “использование псевдо указателей”;
Использовать рабочие указатели.
Спомощью указателей можно обрабатывать как одномерные, так и многомерные массивы. В качестве примера рассмотрим программу подсчета среднеарифметических значений в строках матрицы. Поскольку указатель на первый элемент в строке матрицы является указателем на строку, то для обработки строк матрицы как одномерных массивов оформлена функция с именем average, в которую передаются указатели на первые элементы строк.
//Вычисление средних арифметических значений по строкам матрицы.
#include "stdafx.h" #include <conio.h> #include <iostream> using namespace std; #define ROW 2 #define COL 3
87
double average(double x[ ], int m); // Прототип функции void _tmain()
{
double a[ROW][COL], *pt, *qt;
int i = 0; // Эта переменная для подсказки ввода № строки /* Если в объявленной матрице указан только один индекс, то это указатель на строку матрицы, например, qt = a[0]; */
for (qt = a[0]; qt < a[0] + ROW * COL; qt += COL )
{
cout << "Введите строку матрицы " << (i + 1) << endl;
for (pt = qt; pt < qt + COL; pt++) cin >> *pt;
i++; // Подсчет номера строки
}
cout << "\nСредние арифметические по строкам" << endl;
for (qt = a[0]; qt < a[0] + ROW * COL; qt += COL ) cout << average (qt, COL) << endl;
_getch();
}
//Функция вычисления среднего арифметического для вектора
//x[ ] – одномерный массив, m - длина массива
double average(double x[ ], int m)
{
double *pt, sum = 0; for(pt = x; pt <x + m; pt++)
sum += *pt; return sum / m;
}
В примере рабочий указатель qt настраивается на начало каждой строки матрицы (когда после имени объявленной матрицы указывается только один индекс, то это указатель на строку матрицы с этим