it-roy-ru.com

Проверка на нулевой указатель в C / 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)

это просто абсолютная боль, чтобы найти и отладить.

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

139
Bryan Marble

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

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

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

183
RBerteig

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

50
wilx

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

В C++

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

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

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

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

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

if(some_ptr){;}

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

if(some_ptr != 0){;}

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

if(some_ptr != NULL){;}

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

if(some_ptr != nullptr){;}

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

В С

С другой зверь.

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

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

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

24
M2tM

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

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

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

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

19
GManNickG

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

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

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

    // p is not null
    return 0;
}

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

9
James McNellis

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

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

7
Daniel Hershcovich

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

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

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

7
Mark Ransom

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

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

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

4
dwo

Язык программирования C (K & R) попросит вас проверить на null == ptr, чтобы избежать случайного назначения.

4
Derek

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

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

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

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

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

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

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

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

3
Darryl

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

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

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

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

или же

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

Однако, если бы я писал набор руководств по стилю, я бы не включал в них такие вещи, я бы писал такие:

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

1
JeremyP

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

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

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

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

1
sth

Я большой поклонник того факта, что C/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.

1
Donotalo

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

1
Jens Gustedt