Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Konspekt_S__Chast_2.doc
Скачиваний:
31
Добавлен:
09.02.2015
Размер:
1 Mб
Скачать

Арифметика указателей

К указателям можно применять некоторые арифметические операции. К таким операциям относятся: +, -,++,--. Результаты выполнения этих операций по отношению к указателям существенно отличаются от результатов соответствующих арифметических операций, выполняющихся с обычными числовыми данными.

Рассмотрим следующий пример:

int A = 20, B = 30;

int *p1 = &A;

Пусть переменные A иBрасположены в памяти, например, так, как это показано на следующем рисунке:

A = 20

B = 30

p1 = 100

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

Указатель p1содержит адрес переменнойA, который равен 100 и *p1будет равно значению переменнойA, то есть 20. Выполним следующую операцию:

p1 = p1 + 1;

или, что то же самое:

p1++;

Значение указателя изменится и станет равным 104, а не 101, как, наверное, ожидалось. То есть теперь указатель ссылается уже на переменную B и значение *p1 будет равно 30.

Таким образом, добавление или вычитание 1 из указателя приводит к изменению его значения на размер базового типа указателя. В общем случае, например, при выполнении следующей операции:

p1 = p1 + N; // N – некоторое целое значение

значение указателя увеличится на sizeof(<базовый тип указателя>) * Nи в нашем случае это приращение будет равноsizeof(int) * N = 4 * N. Так, еслиN= 4, аp1 = 100 , то значение указателяp1увеличится на 16 и станет равно 116, и указатель будет ссылаться на данные, расположенные по адресу 116.

Внимание. Добавлять к указателям или вычитать из указателей можно только целые значения.

Поскольку упомянутые арифметические операции выполняются по-разному при их применении к указателям и обычным арифметическим типам данных, а также учитывая высший приоритет операции *, при использовании указателей в составе выражений следует внимательно обращаться со скобками. Например, выражения (см. предыдущий рисунок)

*(p1 + 1)и*p1 + 1

имеют совершенно разный смысл. Первое выражение даст значение 30, а второе выражение будет равно 21 (в первом выражении сначала изменяется адрес, а затем осуществляется обращение в память по этому измененному адресу; во втором выражении мы обращаемся по старому адресу и к значению, хранящемуся по этому адресу добавляем 1).

Ошибки при работе с указателями

Указатели – это очень мощное, полезное, но и очень опасное средство. Ошибки, которые возникают при неправильном использовании указателей, кроме того, что могут приводить к серьезным и непредсказуемым ошибкам в работе программы, еще и очень трудно диагностировать (обнаруживать).

Основная и наиболее часто встречающаяся ошибка при работе с указателями связана с использованием неинициализированных указателей.

Рассмотрим следующий пример:

int *p1;

*p1 = 1001;

cout << *p1;

Хотя с точки зрения синтаксиса этот фрагмент программы корректен, попытка его выполнения закончится, скорее всего, плачевно. Когда мы определяем указатель (int *p1;), в некотором участке памяти создается обычная переменная – указатель, но поскольку значения этой переменной никакого не присвоено (она не инициализирована), то ее значение будет соответствовать тем случайным данным (“мусору”), которые содержались в этом участке памяти. Таким образом, неинициализированный указатель будет содержать некоторый случайный адрес. Дальнейшие попытки обратиться по этому адресу в память могут привести к одному из двух неприятным последствиям. Если это случайное значение адреса будет указывать на недопустимую область памяти (например, за пределами памяти, выделенной для нашей программы), то возникнет ошибка времени выполнения, и программа аварийно завершит свою работу. Но может быть и хуже. Если случайно значение указателя будет содержать адрес, принадлежащей области памяти нашей программы, то произойдет непредсказуемое изменение данных программы. Она (программа) может продолжить свою работу, а последствия такого несанкционированного изменения в программе могут сказаться значительно позднее и вызвать некорректное поведение программы. Обнаружить причину возникновения подобных ошибок чрезвычайно трудно.

Для того, чтобы минимизировать последствия подобных ошибок, необходимо при определении указателя выполнить его инициализацию. Если заранее не известно, какое конкретное значение должен иметь указатель, то его следует инициализировать нулевым значением:

int *p1 = 0;

По крайне мере в этом случае, если мы в дальнейшем забудем присвоить этому указателю конкретное нужное нам значение, попытка обращения по нулевому адресу обязательно приведет к аварийному завершению работы программы. Такую ошибку найти будет значительно проще, чем искать причину некорректной работы программы.

Более того, при нулевой инициализации указателя перед обращением к этому указателю мы всегда можем выполнить проверку на наличие в указателе конкретного адреса:

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]