Что из этого создаст нулевой указатель? - PullRequest
14 голосов
/ 25 марта 2010

Стандарт говорит, что разыменование нулевого указателя ведет к неопределенному поведению. Но что такое «нулевой указатель»? В следующем коде мы называем «нулевой указатель»:

struct X
{
  static X* get() { return reinterpret_cast<X*>(1); }
  void f() { }
};

int main()
{
  X* x = 0;
  (*x).f(); // the null pointer?  (1)

  x = X::get();
  (*x).f(); // the null pointer?  (2)

  x = reinterpret_cast<X*>( X::get() - X::get() );
  (*x).f(); // the null pointer?  (3)

  (*(X*)0).f(); // I think that this the only null pointer here (4)
}

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

Ответы [ 4 ]

12 голосов
/ 25 марта 2010

Целочисленное константное выражение, которое оценивается как 0, допустимо как нулевой указатель, поэтому первый случай также разыменовывает нулевой указатель.

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

12 голосов
/ 25 марта 2010

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

7 голосов
/ 25 марта 2010

C ++ Standard (2003) 4.10

4.10 Преобразование указателя

1 Константа нулевого указателя является интегральное постоянное выражение (5.19) значение целого типа, которое оценивает в ноль. Константа нулевого указателя может быть преобразованным в тип указателя; Результатом является значение нулевого указателя этот тип и отличается от любое другое значение указателя на объект или указатель на тип функции. Два ноль Значения указателя одного типа должны сравнить равных. Преобразование константа нулевого указателя на указатель на cv-квалифицированный тип является единственным преобразование, а не последовательность преобразование указателя с последующим преобразование квалификации (4.4).

5.2.10 Повторное толкование акта

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

1) X* x = 0; (*x).f(); Да. 0 является целочисленным константным выражением и преобразуется в константу нулевого указателя. Затем константа нулевого указателя может быть преобразована в значение нулевого указателя.

2) x = X::get(); нет, см. Примечание 64 в п. 5.2.10

3) x = reinterpret_cast<X*>( X::get() - X::get() ); нет, см. Примечание 64 в п. 5.2.10

4) ( (X ) 0) .f (); Да. 0 (целочисленное константное выражение) -> константа нулевого указателя -> значение нулевого указателя.

0 голосов
/ 25 марта 2010
X* x = 0;
(*x).f(); // the null pointer?  (1)

Я думаю, что это квалифицируется как разыменование, хотя f() фактически никогда не использует указатель this, и в X нет виртуальных методов. Мой рефлекс должен был сказать, что это крушение, но теперь, когда я об этом думаю, я не уверен в этом.

x = X::get();
(*x).f(); // the null pointer?  (2)

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

x = reinterpret_cast<X*>( X::get() - X::get() );
(*x).f(); // the null pointer?  (3)

Скомпилируется ли выражение X::get() - X::get()? Я не думал, что законно было вычесть указатель из другого указателя, подобного этому.

РЕДАКТИРОВАТЬ: D'Oh! Конечно это законно. О чем я только думал? Я ясно, что я бордовый.

...