Избегание исключений с нулевым указателем в большой базе кода c ++

StackOverflow https://stackoverflow.com/questions/1180832

  •  19-09-2019
  •  | 
  •  

Вопрос

Я унаследовал большую базу кода c ++, и у меня есть задача избегать любых исключений с нулевым указателем, которые могут возникнуть в базе кода.Существуют ли доступные инструменты статического анализа, я думаю, lint, которые вы успешно использовали.

На какие еще вещи вы обращаете внимание?

Это было полезно?

Решение

Вы можете начать с устранения источников NULL:

Изменение

if (error) {
    return NULL;
}

В

if (error) {
    return DefaultObject; // Ex: an empty vector
}

Если возврат объектов по умолчанию неприменим и ваша кодовая база уже использует исключения, выполните

if (error) {
    throw BadThingHappenedException;
}

Затем добавьте обработку в соответствующих местах.

Если вы работаете с устаревшим кодом, вы могли бы создать некоторые функции-оболочки / классы:

ResultType *new_function() {
    ResultType *result = legacy_function();
    if (result) {
        return result;
    } else {
        throw BadThingHappenedException;
    }
}

Новые функциональные возможности должны начать использовать новые функции и иметь надлежащую обработку исключений.

Я знаю, что некоторые программисты просто не получают исключений, включая таких умных людей, как Джоэл.Но что в конечном итоге происходит при возврате NULL, так это то, что этот NULL передается как сумасшедший, поскольку все просто подумают, что это не их дело - обрабатывать его и молча возвращать.Некоторые функции могут возвращать код ошибки, и это нормально, но вызывающий часто в конечном итоге возвращает еще один NULL в ответ на ошибки.Затем вы видите множество проверок на НУЛЬ в каждой отдельной функции, независимо от того, насколько тривиальна функция.И все, что требуется, - это всего лишь ОДНО место, которое не проверяет наличие NULL, чтобы завершить работу программы.Исключения заставляют вас тщательно обдумать ошибку и точно решить, где и как с ней следует обращаться.

Похоже, вы просто ищете простые решения, такие как инструменты статического анализа (которые вы всегда должны использовать).Изменение указателей на ссылки также является отличным решением.Однако в C ++ есть прелесть RAII, которая устраняет необходимость в том, чтобы везде было "try {} finally {}", поэтому я думаю, что это заслуживает вашего серьезного рассмотрения.

Другие советы

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

Я бы также посмотрел на что-то вроде Очищать который будет использовать ваш код для обнаружения повреждения памяти.

Во-первых, с технической точки зрения, C ++ не имеет исключений с нулевым указателем.Разыменование нулевого указателя имеет неопределенное поведение и в большинстве систем приводит к внезапному завершению программы ("аварийному завершению").

Что касается инструментов, я тоже рекомендую задать этот вопрос:

Стоят ли инструменты статического анализа кода C ++ того?

Что касается разыменования нулевого указателя, в частности, учтите, что разыменование нулевого указателя состоит из трех основных элементов:

  1. Введение нулевого указателя.
  2. Поток этого указателя в другом месте программы.
  3. Разыменование этого указателя.

Самая сложная часть для инструмента статического анализа - это, конечно, шаг 2, и инструменты различаются по тому, насколько сложный путь они могут точно (т. Е. без слишком большого количества ложных срабатываний) отследить.Возможно, было бы полезно ознакомиться с некоторыми конкретными примерами ошибок, которые вы хотите исправить, чтобы лучше определить, какой инструмент будет наиболее эффективным.

Отказ от ответственности:Я работаю на Coverity.

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

    #ifdef  NDEBUG

    #define NotNull(X) X

    #else // in Debug-Mode

    template<typename T> class NotNull;

    template<typename T> // template specialization only for pointer-types
    class NotNull<T*> {
    public:
        NotNull(T* object)
        : _object(object) {
            assert(object);
        }

        operator T*() const {
            return _object;
        }

        T* operator->() const {
            return _object;
        }
    private:
        T *_object;
    };

    #define NotNull(X) NotNull<X>

    #endif // in Debug-Mode

Вы просто меняете каждую функцию из этого:

void increase(int* counter)  
{ .. } 

к этому

void increase(NotNull(int*) counter)
{ .. }  

p.s :впервые найденный ЗДЕСЬ и может быть доработан еще больше

Это может представлять интерес:

Какие инструменты статического анализа C ++ с открытым исходным кодом доступны?

Инструмент статического анализа кода C ++ в Windows

Стоят ли инструменты статического анализа кода C ++ того?

Я бы также рассмотрел возможность использования динамического инструмента выполнения, такого как Валгринд (бесплатно)

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

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

В идеале вам нужны правильные утверждения по крайней мере в течение периода разработки , поэтому рассмотрите возможность использования макроса для скрытия или переопределения утверждения для производственных сборок

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top