Проверка указателя NULL в C/С++

В последнем обзоре кода участник пытается обеспечить, чтобы все проверки NULL на указателях выполнялись следующим образом:

int * some_ptr;
// ...
if (some_ptr == NULL)
{
    // Handle null-pointer error
}
else
{
    // Proceed
}

вместо

int * some_ptr;
// ...
if (some_ptr)
{
    // Proceed
}
else
{
    // Handle null-pointer error
}

Я согласен с тем, что его способ немного более ясен в том смысле, что он явно говорит "Удостоверьтесь, что этот указатель не является NULL", но я бы возразил, сказав, что любой, кто работает над этим кодом, поймет, что с помощью указателя переменная в выражении if неявно проверяется на NULL. Кроме того, я чувствую, что второй метод имеет меньший шанс ввести ошибку ила:

if (some_ptr = NULL)

что является абсолютной болью для поиска и отладки.

Какой способ вы предпочитаете и почему?

Ответ 1

По моему опыту, предпочтительны тесты формы if (ptr) или if (!ptr). Они не зависят от определения символа NULL. Они не предоставляют возможности для случайного назначения. И они ясны и кратки.

Изменить: Как отмечает SoapBox в комментарии, они совместимы с классами С++, такими как auto_ptr, которые являются объектами, которые действуют как указатели и которые обеспечивают преобразование в bool, чтобы включить эта идиома. Для этих объектов явное сравнение с NULL должно было бы вызвать преобразование в указатель, который может иметь другие смысловые побочные эффекты или быть более дорогостоящим, чем простая проверка существования, что подразумевает преобразование bool.

Я предпочитаю код, который говорит, что это означает, без ненужного текста. if (ptr != NULL) имеет то же значение, что и if (ptr), но за счет избыточной специфичности. Следующая логическая вещь - написать if ((ptr != NULL) == TRUE), и в этом заключается безумие. Язык C ясен, что логическое значение, протестированное с помощью if, while или тому подобное, имеет конкретное значение ненулевого значения, которое истинно, а ноль - false. Резервирование не делает его более ясным.

Ответ 2

if (foo) достаточно ясен. Используйте его.

Ответ 3

Я использую if (ptr), но об этом не стоит спорить.

Мне нравится мой путь, потому что он краткий, хотя другие говорят, что == NULL упрощает чтение и более явное. Я вижу, откуда они берутся, я просто не согласен с тем, что дополнительный материал облегчает его. (Я ненавижу макрос, поэтому я предвзятый.) До вас.

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

Обратите внимание, что в С++ 0x мы можем сделать if (ptr == nullptr), что для меня лучше подходит. (Опять же, я ненавижу макрос, но nullptr хорош.) Я все еще делаю if (ptr), хотя, просто потому, что это то, к чему я привык.

Ответ 4

Я начну с этого: последовательность - это король, решение менее важно, чем согласованность в вашей базе кода.

В С++

NULL определяется как 0 или 0L в С++.

Если вы читали язык программирования С++ Bjarne Stroustrup предлагает использовать 0 явно, чтобы избежать макроса NULL при выполнении назначения, Я не уверен, что он сделал то же самое со сравнениями, но прошло некоторое время с тех пор, как я прочитал книгу, я думаю, что он просто сделал if(some_ptr) без явного сравнения, но я не понятен.

Причиной этого является то, что макрос NULL обманчив (как почти все макросы), он фактически 0 литерал, а не уникальный тип, как следует из названия. Избегание макросов является одним из общих рекомендаций на С++. С другой стороны, 0 выглядит как целое число, и оно не сравнивается с указателями или не назначается им. Лично я мог идти в любом случае, но обычно я пропускаю явное сравнение (хотя некоторым людям это не нравится, вероятно, поэтому у вас есть вкладчик, предлагающий изменение в любом случае).

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

Это понятно и распространенная идиома, и я предпочитаю ее, нет возможности случайно присвоить значение во время сравнения, и она ясно читает:

if(some_ptr){;}

Это понятно, если вы знаете, что some_ptr является типом указателя, но может также выглядеть как целочисленное сравнение:

if(some_ptr != 0){;}

Это ясно, в обычных случаях это имеет смысл... Но это нечеткая абстракция, NULL фактически 0 буквальна и может быть легко использована неправильно:

if(some_ptr != NULL){;}

С++ 0x имеет nullptr, который теперь является предпочтительным методом, поскольку он явный и точный, просто будьте осторожны при случайном назначении:

if(some_ptr != nullptr){;}

До тех пор, пока вы не сможете перейти на С++ 0x, я бы сказал, что это пустая трата времени, беспокоясь о том, какой из этих методов вы используете, но все они недостаточны, поэтому был изобретен nullptr (наряду с типичными проблемами программирования, которые возникли с отличной переадресацией.) Самое главное - поддерживать согласованность.

В C

C - другой зверь.

В C NULL может быть определено как 0 или as ((void *) 0), C99 допускает реализацию определенных констант нулевого указателя. Таким образом, это фактически сводится к определению NULL реализации, и вам нужно будет проверить его в своей стандартной библиотеке.

Макросы очень распространены, и в целом они используются много, чтобы восполнить недостатки в поддержке общего программирования на языке и других вещах. Язык намного проще и более распространен на препроцессоре.

С этой точки зрения я, вероятно, рекомендую использовать определение макроса NULL в C.

Ответ 5

Честно говоря, я не понимаю, почему это имеет значение. Любой из них достаточно ясен, и любой, кто имеет умеренный опыт работы с C или С++, должен понимать и то, и другое. Один комментарий, хотя:

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

int f(void* p)
{
    if (!p) { return -1; }

    // p is not null
    return 0;
}

Таким образом, вы избегаете "кода стрелки".

Ответ 6

Собственно, я использую оба варианта.

Есть ситуации, когда вы сначала проверяете правильность указателя, и если он NULL, вы просто возвращаете/выходите из функции. (Я знаю, что это может привести к обсуждению "если функция имеет только одну точку выхода" )

В большинстве случаев вы проверяете указатель, затем выполняете то, что хотите, а затем разрешаете ошибку. Результатом может быть уродливый x-times с отступом код с несколькими if.

Ответ 7

Лично я всегда использовал if (ptr == NULL), потому что он делает мое намерение явным, но на данный момент это просто привычка.

Использование = вместо == будет захвачено любым компетентным компилятором с правильными настройками предупреждения.

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

Ответ 9

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

И не это важно, но мое личное предпочтение - if (ptr). Для меня смысл становится более очевидным, чем даже if (ptr == NULL).

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

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

if (error_condition)
  bail_or_fix();
  return if not fixed;

// If I'm still here, I'm on the happy path

Ухаживая за необычным состоянием, я могу позаботиться об этом, а потом забуду об этом. Но если я не смогу вернуться на счастливый путь, обработав его спереди, тогда его следует обработать после основного случая, потому что он делает код более понятным. По-моему.

Но если это не в руководстве по стилю, тогда это просто мое мнение, и ваше мнение справедливо. Либо стандартизируйте, либо нет. Не позволяйте рецензентам псевдостандартизировать только потому, что он получил мнение.

Ответ 10

Еще один момент в пользу практики foo == NULL: Если foo есть, скажем, int * или bool *, тогда проверка if (foo) может быть случайно интерпретирована читателем как проверяющая значение pointee, т.е. Как if (*foo). Сравнение NULL здесь напоминает нам о указателе.

Но я полагаю, что хорошее соглашение об именах делает этот аргумент спорным.

Ответ 11

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

Ответ 12

Я думаю, это совершенно ясно:

if( !some_ptr )
{
  // handle null-pointer error
}
else
{
  // proceed
}

Прочитайте его как "Если нет указателя..."

Кроме того, он является кратким и не представляет опасности случайных назначений.

Ответ 13

Это одна из основ обоих языков, которые указатели оценивают по типу и значению, которые могут использоваться как управляющее выражение, bool в С++ и int в C. Просто используйте его.

Ответ 14

Я большой поклонник того факта, что C/С++ не проверяет типы в булевых условиях в операторах if, for и while. Я всегда использую следующее:

if (ptr)

if (!ptr)

даже для целых чисел или другого типа, который преобразуется в bool:

while(i--)
{
    // Something to do i times
}

while(cin >> a >> b)
{
    // Do something while you've input
}

Кодирование в этом стиле более читаемо и яснее для меня. Просто мое личное мнение.

Недавно, работая на микроконтроллере OKI 431, я заметил, что следующее:

unsigned char chx;

if (chx) // ...

более эффективен, чем

if (chx == 1) // ...

потому что в более позднем случае компилятор должен сравнить значение chx с 1. Если chx является только флагом true/false.

Ответ 15

  • Указатели не являются булевыми
  • Современные компиляторы C/С++ выдают предупреждение, когда вы пишете if (foo = bar) случайно.

Поэтому я предпочитаю

if (foo == NULL)
{
    // null case
}
else
{
    // non null case
}

или

if (foo != NULL)
{
    // non null case
}
else
{
    // null case
}

Однако, если бы я писал набор правил стиля, я бы не стал помещать в него такие вещи, я бы поставил такие вещи, как:

Убедитесь, что вы указали нулевой указатель.