C ++ исключение для нулевых указателей - PullRequest
5 голосов
/ 11 марта 2011

У меня есть маленькая функция (в DLL), которая выглядит следующим образом:

int my_function(const char* const s)
{
    try {
        return my_object->do_something_with(s);
    } catch (exception& e) {
        return ERROR_CODE;
    }
}

Я думал, что блок try-catch предотвратит распространение всего, что может произойти внутри my_object, на улицу. К сожалению, я ошибся, и моя программа, которая вызывает эту функцию (из VB), просто перестала работать, потому что я передал аргумент нулевого указателя.

Итак, почему мой блок try-catch не работает так, как ожидалось (I)? Есть ли обходной путь? Раньше я много программировал на Java, и я думаю, что это сработало бы там ...

Ответы [ 6 ]

10 голосов
/ 11 марта 2011

Это сработало бы в Java, потому что в Java NullPointerException - это thown.В C ++ нет такого повсеместного исключения, разыменование нулевого указателя просто вызовет ошибку сегментации.

Кроме того, в C ++ нет принудительной иерархии типов исключений.Исключение любого типа может быть брошено;исключения не должны происходить от std::exception.(Таким образом, даже если кто-то определил class NullPointerException {};, но не назначил его производным от std::exception, вы все равно не поймете его.)

5 голосов
/ 11 марта 2011

Разыменование нулевого указателя в C ++: неопределенное поведение .Это означает, что стандарт C ++ не накладывает никаких ограничений на то, что может произойти.Все разрешено - конкретная реализация может задокументировать свое поведение, а может и нет.Если это не так, поведение может быть последовательным и предсказуемым, а может и нет.

В общем случае лучше предположить, что неопределенное поведение вызовет возгорание вашего компьютера (или отформатировать жесткий диск).Только не провоцируй это.Конечно, трудно избежать ошибок в первом фрагменте кода, поэтому, когда вы делаете это случайно, и ваш компьютер не загорается, вам повезло, исправьте ошибку и продолжайте.Ваша функция может быть переписана:

int my_function(const char* const s)
{
    if (s == 0) return ERROR_CODE;
    return my_object->do_something_with(s);
}

В качестве альтернативы вы можете исправить вызывающую функцию так, чтобы она не пропускала нулевой указатель, или вы можете исправить do_something_with, чтобы принять нулевой указатель.Функции C ++ с параметрами указателя должны документировать, разрешены или нет нулевые указатели.

Разные люди будут писать тест (s==0) по-разному - это может быть (s==NULL) или (NULL==s) или (!s).Выберите тот, который вам нравится.

На практике, разумеется, у компиляторов есть веские причины не запускать ваш компьютер, а современные ОС стараются сделать так, чтобы приложения с плохим поведением не причиняли значительных неудобств другим приложениям.Так что в большинстве систем в большинстве случаев, когда вы разыменовываете нулевой указатель, вы получаете какое-то аппаратное исключение или сигнал, или ваш процесс будет остановлен, или, возможно, во встроенной системе устройство заблокируется и потребуется перезагрузка..

В общем случае вы не получите исключение программного обеспечения в стиле C ++, которое вы можете перехватить с помощью любого типа предложения catch, хотя в Windows есть функция SEH, которую вы можете использовать для получения такого поведения.

2 голосов
/ 11 марта 2011

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

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

Обратите внимание, что обычно перехват всех не обрабатывает исключения более низкого уровня, такие как нарушения доступа.Если вы используете Windows и Visual Studio, вы можете настроить свой проект таким образом, чтобы блоки общего назначения обрабатывали эти исключения с помощью / EHa, но это часто опасно и не рекомендуется.

1 голос
/ 11 марта 2011

c ++ faq относительно исключений может помочь вам лучше понять исключения c ++.

У многих людей, программирующих на java, есть проблемы с пониманием указателей и ссылок, поэтому, если программа перестала работать, возможно,это было из-за сбоя (ошибка сегментации из-за разыменования NULL?)

0 голосов
/ 11 марта 2011

Примите это с недоверием, потому что я не касался компилятора C ++ с 1999 года. С STL и C ++ многое изменилось, что, возможно, все изменилось. Тем не менее, это то, что я помню.

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

В Java вызов для отмены ссылки на ссылку, в конечном счете, является инструкцией байт-кода, отправляемой и интерпретируемой JVM. JVM всегда проверяет (внутренне), является ли ссылка нулевой, и когда она обнаруживает, что это так, она автоматически генерирует исключение java.lang.NullPointerException.

Если бы JVM не была запрограммирована на такую ​​вещь, доступ к нулевой ссылке вызвал бы сбой JVM с SEGFAULT - именно то поведение, которое можно было бы ожидать с компилированным неохраняемым языком при обнаружении неохраняемого нулевого указателя. разыменование (не только C или C ++, но также в Pascal, Ada, Assembly или даже в старом добром BASIC, пытающемся сделать PEEK / POKE в неверном месте адреса.)

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

Это несколько эквивалентно следующему псевдокоду Java

if( myPossiblyBadReference == null )
{
   throw java.lang.NullPointerException( "you suck!" );
  // or throw my.own.NPEException(); // preferably a specialization of NullPointerException
}
else
{
   doYourThingieWith( myPossiblyBadReference );
}

Теперь все может быть сделано по-другому в C ++, особенно сейчас, когда материал C ++ 0x идет по каналу (о котором я понятия не имею.) Но, по крайней мере, с этим поведением мне пришлось столкнуться при кодировании в C ++.

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

JVM - это барьер, который дает вам много хороших возможностей по обработке ошибок (на которые мы, Java-разработчики, склонны слишком полагаться). Эти возможности по обработке ошибок должны быть явно закодированы, когда вы работаете на более низком уровне. уровень (не только в C ++.)

0 голосов
/ 11 марта 2011

В C ++ нет исключения NullPointerException. Если вы действительно хотите перехватить такой доступ, вы должны перехватить некоторые сигналы (например, SIGSEGFAULT).

...