создание и проверка указателя на оператор if - PullRequest
4 голосов
/ 24 ноября 2011

Этот код правильный?

void foo ( int* p )
{
  if ( int* p2 = p ) // single "="
  {
    *p2++;
  }
}

Я всегда думал, что это не так, но недавно я видел такой код в моих источниках коллеги.

Что, если "p" равен NULL? MS VS 2008 работает правильно, но показывает «предупреждение C4706: назначение в условном выражении».

Спасибо.

Ответы [ 8 ]

7 голосов
/ 24 ноября 2011

Предупреждение assignment within conditional expression обычно выдается компиляторами для предотвращения случаев, когда вы пишете

 if (a = b) 
 {

где вы имели в виду

 if (a == b) // big difference!
 {

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

 {
      int *p2 = p;
      if (p2)
      {

      }
 }

и нет риска , что вместо этого вы хотели бы получить синтаксическую ошибку (int *p2 == p?!):)

Остальная часть вашего поста является абсолютно верной для C ++ 03 (и более поздних версий) и выполняет только то, что написано 1


1 (что с точки зрения длительного эффекта, не так много, потому что

  • p2 разыменовывается без того, чтобы когда-либо не делал что-то с ним
  • p2 увеличивается, никогда не делает что-то с ним,

но я думаю, это всего лишь пример кода? Если это не очевидно, *p2++ эквивалентно *p2; p2++;)

4 голосов
/ 24 ноября 2011

Вызывая предупреждение C4706 , компилятор просто спрашивает, действительно ли вы намеревались написать if ( int* p2 == p ) вместо if ( int* p2 = p ), как показано.

Согласно 6.4 стандарта C ++ 2003 года, if ( int* p2 = p ) является законным:

Значение условия [if (condition)], которое является инициализированным объявление в выражении, отличном от switch, является значение объявленной переменной неявно преобразуется в тип bool.

Если p равно NULL, условие (int* p2 = p) не выполняется, поскольку значение p2 равно 0 и, следовательно, неявно false, а *p2 равно не увеличивается.

3 голосов
/ 24 ноября 2011

Это формально правильно (в C ++, но не в C).Разрешение определения в качестве условия было добавлено с dynamic_cast для поддержки таких вещей, как:

if ( Derived* p = dynamic_cast<Derived*>( ptrToBase ) ) {
    //  ...
}

Аргумент состоял в том, что никогда не будет момента, когда p находится в области действия
,ненулевой.После if, p больше не находится в области видимости.

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

if ( Derived1* p = dynamic_cast<Derived1*>( ptrToBase ) ) {
    // ...
}
if ( Derived2* p = dynamic_cast<Derived2*>( ptrToBase ) ) {
    // ...
}
// ...

Но на самом деле это не то, что вы хотите сделать в хорошем коде.

В общем, я бы избежал конструкции, заменив ее начто-то вроде:

int* p2 = p;
if ( p2 != NULL ) {
    //  ...
}

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

EDIT:

Что касается предупреждения: это полный идиотизм со стороны компилятора.В условном выражении нет присваивания: нет присваивания, точки и нет «условного выражения», условие в оригинале - это объявление, а не выражение. Если компилятор хочет что-то предупредить (по стилистическим соображениям, поскольку он согласен со мной, что это плохой стиль), то он должен предупредить о неявном преобразовании в bool.(И, конечно, то, должно ли быть выдано это предупреждение, должно контролироваться опцией. Компилятор должен компилировать язык, а не навязывать стиль, если только один не попросит его явно предупредить о конкретных проблемах стиля.)

2 голосов
/ 24 ноября 2011

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

void foo ( int* p )
{
  int* p2 = p;
  if ( p2 ) 
  {
    *p2++;
  }
}

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

1 голос
/ 24 ноября 2011

Этот код кажется странным.Это правильно, но я не вижу, в каких случаях это полезно.
Это менее читабельно, чем простые альтернативы (как упоминал Орен С.), и без необходимости определяет новую переменную для увеличения (* p).Параметр p уже является новой переменной в стеке, зачем создавать другую?

1 голос
/ 24 ноября 2011

Если p равно NULL, условие не выполняется.Так что код в порядке.

0 голосов
/ 24 ноября 2011

Я думаю, что грамматически код не имеет проблем. Но это не хороший стиль. Если вы точно знаете, что есть одиночный "=", вы также можете сделать следующее:

if ((int* p2 = p) != NULL)
{
    *p2++;
}

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

0 голосов
/ 24 ноября 2011

Посмотрите на int * p, так как это "p - указатель на int". И «p2 также является указателем на int». Вы делаете p2 указателем на указатель, на который указывает p, когда вы говорите p2 = p. Утверждение * p2 ++; увеличит адрес на 4 и укажет на что-то, кроме int, на которое вы изначально указывали. Который должен быть 32-битным целым числом. Указатель увеличивается на 4 на 32-битном компиляторе. Если вы используете (* p2) ++, заключенный в круглые скобки, это увеличит число, на которое указывает указатель.

Да, это работает ... странно.

int ClassPtr::MethodA(int *p)
{
    if (int *p2 = p)
    {
       (*p2)++;
       return *p2;
    }
          return 0; 
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...