Рекомендации по устранению проблем с указателями
Каковы реалистичные результаты ошибок программистов, относящихся к указателям?
Какие "плохие последствия" возникают, когда программисты создают ошибки указателя?
Предпочтительные примеры с кодом.
Ответы
Ответ 1
Вещи, которые могут ошибаться при неправильном использовании указателей:
-
Утечки памяти. Вы выделяете указатель в методе, а затем выходите из области действия без надлежащего освобождения. Указатель на память в куче теперь потерян, но память остается выделенной. Освободить эту память сейчас чрезвычайно сложно. Дополнительная информация из Википедии.
-
Нарушения доступа. Вы создаете указатель, указывающий на адрес памяти, к которому у вас нет доступа, или который не существует. Указатели являются целыми числами, и их можно манипулировать, как и любое другое число. Когда вы попытаетесь разыменовать свой неверный указатель, ваша программа остановится. Дополнительная информация из Википедии.
-
Ошибки нулевого указателя - это особый случай нарушения прав доступа. Правильный способ "припарковать" указатель, чтобы он не указывал ни на что конкретно, заключается в том, чтобы установить его значение в ноль или нуль. Попытка разыменовать нулевой указатель остановит вашу программу. Дополнительная информация из Википедии.
-
Переполнение буфера. Вы выделяете указатель для символьного буфера в 30 символов. Затем вы переходите к потоку ввода пользователя (из сокета, файла, консоли и т.д.) В этот буфер. Если вам не удалось должным образом реализовать проверки ограничения буфера, ваша программа потенциально может помещать в буфер более 30 символов. Это повредит любые данные, хранящиеся рядом с буфером в памяти, и, возможно, подвергает вас вредоносному коду. Дополнительная информация из Википедии.
-
Повреждение памяти - указатель представляет собой целое число, содержащее адрес памяти того, на что он указывает. В качестве целого числа арифметика указателя может использоваться для управления значением указателя различными способами. Тонкие ошибки могут развиться, если вычисления указателей идут не так. Теперь указатель укажет на неизвестное место в памяти, и все может произойти, когда оно разыменовано.
-
Проблемы с строкой с нулевым завершением - эти ошибки возникают, когда функции библиотеки строк, которые ожидают строки с нулевым завершением, передаются указателями символов, которые не имеют нулевого завершения. Функции библиотеки строк будут продолжать обрабатывать символы, по одному за раз, пока не будет найдено нуль - где бы это ни было. Шутка лучше всего иллюстрирует эту ошибку.
Ответ 2
От http://xkcd.com
Я предполагаю, что я беру запрос на иллюстрацию буквально.
Ответ 3
Все это сводится к доступу к областям памяти, не предназначенным для этого. Чтение/запись вне выделенной области, разыменование неинициализированных указателей. Это в основном это.
Там также неверно интерпретируется тип объекта, на который указывает, но это обычно требует некоторых усилий, чтобы уйти, не закричав компилятором.
И утечка памяти, но эта другая история, это о распределении, а не указатели как таковые.
Ответ 4
Просто инициализация переменные указателя и хорошая очистка устраняют 99% ваших проблем. Хорошая очистка, я имею в виду; освобождение памяти и установка переменных указателя в значение null.
В противном случае вам понадобится прозрачный дизайн для прокручивания указателей и какой код отвечает за очистку этой памяти. Если вы окажетесь в ситуации, когда вы не знаете, какой код будет последним, чтобы использовать память и должен быть очисткой, тогда у вас есть запах дизайна, который вы хотите исправить, чтобы поддерживать ваши здравомыслие.
Ответ 5
- Никогда не игнорируйте никаких предупреждений.
-
Используйте статические инструменты анализа, например Splint.
-
Самое главное: используйте инструменты динамического анализа - они часто предупреждают о некорректном использовании указателей, нарушении границ массива и т.д. 'I убедитесь, что у них есть нулевые ошибки, даже если программа, похоже, работает...
Ответ 6
Результаты разыменования плохого указателя undefined, поэтому по определению Anything может случиться, когда вы испортите указатель. Вот почему вы должны избегать их использования, когда это возможно.
Языки C-ish разработаны вокруг использования указателей, и сейчас они доминируют, так что это будет звучать как сумасшедший совет для некоторых. Я бы посоветовал людям изучить языки, предназначенные для минимизации использования указателя и проверки распространенных ошибок, таких как Ada.
Мой любимый анекдот указателя следующий: я когда-то работал в группе во Флориде, которая поддерживала сетевое моделирование полета трех гелиоптеров в Kurtland AFB в Нью-Мексико (большую часть пути на другой стороне континента). Однажды произошла авария с крахом. Локальный веб-сайт не смог его исправить, поэтому через месяц или около того один из наших инженеров перелетел, чтобы посмотреть на него. Через две недели он был сбит с толку, так что другой потянул. Через месяц наш главный инженер тоже прилетел, чтобы помочь.
Еще один месяц спустя (все время, когда компания платила за 3 человека, живущих в отелях, арендуя автомобили и летая каждую пару выходных дней), они отследили проблему. Оказалось, что кто-то индексировал один за концом массива (C тоже не проверяет индекс). Затем они хватали дерьмо, сидящее в этом месте, передавая его второй машине по сети, и она использовала это значение в качестве индекса массива. Поскольку этот код был также в C, снова не проверялся. Он схватил дерьмо в этом месте и отправил его на третью машину. Эта машина использовала дерьмо как указатель и пыталась разыграть его. бум.
Таким образом, ошибка в коде на одной машине приводила к сбою двух машин, удаленных по сети. Тысячи долларов и несколько месяцев драгоценного времени были потрачены впустую, отслеживая его. Все потому, что они использовали язык без проверок диапазона.
Ответ 7
Я не знаю, если вы все еще можете это сделать, но я помню несколько лет назад, когда мы сделали script, чтобы свернуть систему, уничтожив всю RAM. Вот как мы это сделали.
int *i;
while(1){
*i = 0;
i++;
}
По крайней мере, как я помню, мы это сделали. Я считаю, что теперь это не сработает.
Ответ 8
Сырые указатели являются злыми. Невозможно узнать, действительны они или нет (висячие указатели), если они были инициализированы (если они не установлены в NULL при инициализации, они могут отображаться как фактически указывающие на что-то), и очень неясно, кто несет ответственность за освобождение ресурсов они указывают на (например, извлечение вызывающего абонента или функция, возвращающая указатель).
Я бы не стал жить без умных указателей. std:: auto_ptr, когда я передаю право собственности (не забудьте об ответственности), boost:: shared_ptr, когда собственность разделяется, boost:: weak_ptr, когда кто-то только "наблюдает" за ресурсом.
Ответ 9
Лучшая практика - избегать использования указателей как можно больше. Вместо этого используйте управляемый язык для основной части вашего программного обеспечения и только упадите до C для небольших частей, где это необходимо для доступа к системным ресурсам или эффективности. Другими словами, C следует рассматривать так же, как язык ассемблера.
(Первоначальный вопрос, который я помог закрыть, так как "не настоящий вопрос" был совершенно другим и слишком широким, чтобы быть полезным.)