Разыменование нулевой указатель

int* p = 0;
int* q = &*p;

Это поведение undefined или нет? Я просмотрел некоторые связанные вопросы, но этот конкретный аспект не появился.

Ответ 1

Ответ на этот вопрос: зависит от того, какой язык вы следуете: -).

В C90 и С++ это недопустимо, потому что вы выполняете косвенные действия по нулевому указателю (делая *p), и это приводит к поведению undefined.

Однако в C99 это корректно, правильно сформировано и четко определено. В C99, если операнд унарного & был получен в результате применения унарного * или путем выполнения подписи ([]), то ни &, ни * или [] применяется. Например:

int* p = 0;
int* q = &*p; // In C99, this is equivalent to int* q = p;

Аналогично,

int* p = 0;
int* q = &p[0]; // In C99, this is equivalent to int* q = p + 0;

Из C99 §6.5.3.2/3:

Если операнд [унарного оператора &] является результатом унарного оператора *, ни этот оператор, ни оператор & не оцениваются, и результат как бы опускался, за исключением того, что ограничения по операторам все еще применяются, и результат не является lvalue.

Аналогично, если операнд является результатом оператора [], ни оператор &, ни унарный *, который подразумевается [], не оцениваются, и результат выглядит так, как будто & оператор был удален, а оператор [] был заменен на оператор +.

(и его сноска, № 84):

Таким образом, &*E эквивалентно E (даже если E является нулевым указателем)

Ответ 2

Да, это будет поведение undefined, но ваш компилятор может оптимизировать выход &*.

Почему это его undefined, это то, что вы пытаетесь получить доступ к памяти за пределами вашего адресуемого пространства.

Ответ 3

Да, разыменование нулевого указателя имеет поведение undefined. Целочисленная константа 0 в контексте указателя - это нулевой указатель. Что это.

Теперь, если ваша вторая строка была int *q = p;, это было бы простым назначением указателя. Если компилятор удаляет &* и уменьшает разыменование в назначении, вы в порядке.

Ответ 4

ИМХО. Что касается двух строк кода, то нет доступа за пределами адресного пространства. Второй оператор просто берет адрес (* p), который будет "p" снова, и, следовательно, он сохранит "0". Но местоположение никогда не открывается.