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