Программирование на C и C++

Онлайн справочник программиста на C и C++

Проблемы, связанные с указателями

Ничто так не беспокоит, как «дикие» указатели! Указатели предоставляют ужасающую мощь и необходимы в большинстве программ. Но когда указатель содержит неправильное значение, он может вызвать наиболее трудноустранимую ошибку. Сам по себе указатель не вызывает никаких проблем. Проблемы возникают, когда выполняется какая-либо операция, использующая неправильный указатель, например производится чтение или запись в неизвестный участок памяти. При чтении в худшем случае в результате будет прочитан мусор. При записи можно затереть участки кода или данных. В результате этого при поиске ошибки можно найти ее совсем в другом месте. Не существует очевидного способа для разрешения проблем, связанных с указателями.

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

Классическим примером ошибки с указателем является неинициализированный указатель. Например:

/* программа неверна. */
int main (void)
{
int х, *р;
х = 10;
*р = х;
return 0;
}

Данная программа присваивает значение 10 некоторому неизвестному участку памяти. Указатель р не получал адреса памяти, которую можно использовать. Следовательно, он содержит неопределенное значение. Такого рода проблемы часто незаметны, когда программа невелика, поскольку р, скорее всего, содержит «безопасный» адрес, не принадлежащий коду, данным, стеку, куче или операционной системе. По мере роста программы растет и вероятность того, что неправильное использование указателя испортит жизненно важную часть программы. Неожиданно программа может зависнуть. Решение таких проблем совершенно очевидно: следует убедиться, что указатель указывает на некоторую допустимую область. Хотя в таких случаях ошибки достаточно просто обнаружить, частая инициализация указателей (или некорректная инициализация) затрудняет поиск ошибок.

Следующая типичная ошибка возникает из-за недоразумений по использованию указателя. Например, следующая программа имеет фундаментальную ошибку:

#include <stdio.h>
/* программа неверна */
int main(void)
{
int x, *p;
x = 10;
p = x;
printf ("%d", *p);
return 0;
}

Вызов printf() не выводит значения х, которое равно 10, на экран. В результате выводится некоторое неизвестное значение из-за неправильного оператора присваивания

р = х;

Этот оператор присваивает значение 10 указателю р, который должен содержать адрес, а не значение. К счастью, ошибка в данной программе обнаруживается компилятором. Компилятор выдает предупреждение о нетипичном преобразовании указателя. Для устранения ошибки следует написать

p = &х;

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

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