Ошибки при работе с указателями
Указатели – это очень мощное, полезное, но и очень опасное средство. Ошибки, которые возникают при неправильном использовании указателей, кроме того, что могут приводить к серьезным и непредсказуемым ошибкам в работе программы, еще и очень трудно диагностировать (обнаруживать).
Основная и наиболее часто встречающаяся ошибка при работе с указателями связана с использованием неинициализированных указателей.
Рассмотрим следующий пример:
int *p1;
*p1 = 1001;
cout << *p1;
Хотя с точки зрения синтаксиса этот фрагмент программы корректен, попытка его выполнения закончится, скорее всего, плачевно. Когда мы определяем указатель (int *p1;), в некотором участке памяти создается обычная переменная – указатель, но поскольку значения этой переменной никакого не присвоено (она не инициализирована), то ее значение будет соответствовать тем случайным данным (“мусору”), которые содержались в этом участке памяти. Таким образом, неинициализированный указатель будет содержать некоторый случайный адрес. Дальнейшие попытки обратиться по этому адресу в память могут привести к одному из двух неприятным последствиям. Если это случайное значение адреса будет указывать на недопустимую область памяти (например, за пределами памяти, выделенной для нашей программы), то возникнет ошибка времени выполнения, и программа аварийно завершит свою работу. Но может быть и хуже. Если случайно значение указателя будет содержать адрес, принадлежащей области памяти нашей программы, то произойдет непредсказуемое изменение данных программы. Она (программа) может продолжить свою работу, а последствия такого несанкционированного изменения в программе могут сказаться значительно позднее и вызвать некорректное поведение программы. Обнаружить причину возникновения подобных ошибок чрезвычайно трудно.
Для того, чтобы минимизировать последствия подобных ошибок, необходимо при определении указателя выполнить его инициализацию. Если заранее не известно, какое конкретное значение должен иметь указатель, то его следует инициализировать нулевым значением:
int *p1 = 0;
По крайне мере в этом случае, если мы в дальнейшем забудем присвоить этому указателю конкретное нужное нам значение, попытка обращения по нулевому адресу обязательно приведет к аварийному завершению работы программы. Такую ошибку найти будет значительно проще, чем искать причину некорректной работы программы.
Более того, при нулевой инициализации указателя перед обращением к этому указателю мы всегда можем выполнить проверку на наличие в указателе конкретного адреса:
if (p1) // Если указатель не равен 0, то все в порядке
{
*p1 = 1001;
cout << *p1;
}
Else
// Реакция на ошибочную ситуацию
;
Вторая группа ошибок может быть связана с некорректным использованием арифметики указателей. При некорректном выполнении наращивания или уменьшения указателей с помощью операций + или - можно выйти за пределы предполагаемого объекта (например, массива) и в результате получить неверные данные или модифицировать не те значения.
При выполнении операций инкремента и декремента необходимо помнить, что в результате изменяется значение самого указателя и старое его значение теряется.
Третья группа ошибок относится к использованию операций сравнения > или <. Использование этих операций по отношению к указателям может служить для определения относительного расположения в памяти объектов, на которые ссылаются сравниваемые указатели. Однако такое сравнение имеет смысл, если идет сравнение адресов связанных между собой объектов (например, элементов массива, которые гарантированно упорядочены внутри массива). При сравнении с помощью этих операций указателей на несвязанные между собой переменные результат таких сравнений непредсказуем, так как фактическое расположение в памяти не связанных переменных может отличаться от порядка, в котором эти переменные определялись в программе (это зависит от компилятора).
В любом случае правильное использование указателей полностью определяется знаниями программиста и его внимательностью.
Дата добавления: 2019-02-07; просмотров: 651;