В какой момент разыменование нулевого указателя становится неопределенным поведением? - PullRequest
8 голосов
/ 20 августа 2011

Если я на самом деле не обращаюсь к разыменованному «объекту», значит ли разыменование нулевого указателя все еще неопределенным?

int* p = 0;
int& r = *p;    // undefined?
int* q = &*p;   // undefined?

Немного более практичный пример: можно ли разыменовать нулевой указатель, чтобы различать перегрузки?

void foo(Bar&);
void foo(Baz&);

foo(*(Bar*)0);  // undefined?

Хорошо, эталонные примеры определенно неопределенного поведения в соответствии со стандартом:

пустая ссылка не может существовать в четко определенной программе, потому что единственный способ создать такую ​​ссылку - привязать ее к «объекту», полученному путем разыменования нулевого указателя, , который вызывает неопределенное поведение .

К сожалению, выделенная часть неоднозначна. Это обязательная часть, которая вызывает неопределенное поведение, или разыменование часть является достаточной?

Ответы [ 3 ]

6 голосов
/ 20 августа 2011

Я думаю, что второй опус То, что каждый программист C должен знать о неопределенном поведении , может помочь проиллюстрировать эту проблему.

На примере блога:

void contains_null_check(int *P) {
  int dead = *P;
  if (P == 0)
    return;
  *P = 4;
}

Может быть оптимизировано для (RNCE: исключение избыточной проверки нуля):

void contains_null_check_after_RNCE(int *P) {
  int dead = *P;
  if (false)  // P was dereferenced by this point, so it can't be null 
    return;
  *P = 4;
}

Что превращается в оптимизированный (DCE: Устранение мертвого кода):

void contains_null_check_after_RNCE_and_DCE(int *P) {
  //int dead = *P; -- dead store
  //if (false)     -- unreachable branch
  //  return;
  *P = 4;
}

Как вы можете видеть, даже если dead никогда не используется , простое назначение int dead = *P привело к ползучести в программе неопределенного поведения.

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

5 голосов
/ 20 августа 2011
int& r = *p;    // undefined?

Я думаю, что здесь у вас неопределенное поведение, даже если вы не на самом деле используете r (или *p) - объект разыменования.Поскольку после этого шага (т. Е. Разыменование нулевого указателя) поведение программы не гарантируется языком, поскольку программа может сразу аварийно завершить работу, что является одной из возможностей UB.Вы, кажется, думаете, что только чтение значения r с целью использования used в real target вызывает UB.Я так не думаю.

Кроме того, спецификация языка четко говорит "эффект разыменования нулевого указателя" вызывает неопределенное поведение. не говорит "эффект на самом деле с использованием разыменованного объекта из нулевого указателя" вызывает UB.Эффект разыменования нулевого указателя (или другими словами неопределенное поведение ) не означает, что у вас обязательно и немедленно возникнут проблемы, или он должен вылететь немедленно после разыменования нулевого указателя.Нет. Это просто означает, что поведение программы не определено после разыменования нулевого указателя.То есть программа может работать нормально, как и ожидалось, от начала до конца.Или это может произойти сбой сразу или через некоторое время - через несколько минут, часов или дней.Все может случиться в любое время после разыменования нулевого указателя.

4 голосов
/ 20 августа 2011

Да, это неопределенное поведение, потому что спецификация говорит, что «lvalue обозначает объект или функцию» (в пункте 3.10), и это говорит для * -оператора «результат [разыменования] является lvalue, ссылающимся наобъект или функция, на которые указывает выражение "(в пункте 5.3.1).

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...