Является ли сохранение неправильного указателя автоматически undefined поведением?

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

Рассмотрим следующий код:

const char* str = "abcdef";
const char* begin = str;
if (begin - 1 < str) { /* ... do something ... */ }

Выражение begin - 1 означает недопустимый адрес памяти. Обратите внимание, что мы фактически не разыскиваем этот адрес - мы просто используем его в арифметике указателя, чтобы проверить, действительно ли он. Тем не менее, нам все равно придется загружать недопустимый адрес памяти в регистр.

Итак, это поведение undefined? Я никогда не думал, что это так, поскольку много арифметики указателей, похоже, полагаются на подобные вещи, а указатель - это вообще ничто, кроме целого числа. Но в последнее время я слышал, что даже действие загрузки недопустимого указателя в регистр - это поведение undefined, так как некоторые архитектуры автоматически выдают ошибку шины или что-то в этом случае. Может ли кто-нибудь указать мне на соответствующую часть стандарта C или С++, которая так или иначе решена?

Ответ 1

У меня здесь есть стандартный проект, и он делает его undefined бездействия. Он определяет случай ptr + I на 6.5.6/8 для

  • Если операнд указателя указывает на элемент объекта массива, и массив достаточно велик, результат указывает на смещение элемента от исходного элемента, так что разность индексов результирующих и исходных элементов массива равна целочисленное выражение.
  • Кроме того, если выражение P указывает на последний элемент объекта массива, выражение (P) +1 указывает один за последним элементом объекта массива, а если выражение Q указывает один за последним элементом массив, выражение (Q) -1 указывает на последний элемент объекта массива.

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

Ответ 2

Ваш код undefined работает по другой причине:

выражение begin - 1 не выводит неверный указатель. Это поведение undefined. Вам не разрешено выполнять арифметику указателей за пределы массива, над которым вы работаете. Таким образом, само вычитание является недопустимым, а не актом сохранения полученного указателя.

Ответ 3

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

Если вы знаете, что не будете работать на экзотическом оборудовании с такими точными характеристиками, вам не нужно беспокоиться о том, что означает undefined. Он четко определен платформой.

Конечно, пример - плохой стиль, и для этого нет веской причины.

Ответ 4

Любое использование недопустимого указателя дает поведение undefined. У меня нет C-стандарта здесь на работе, но см. "Недопустимые указатели" в Обосновании: http://www.open-std.org/jtc1/sc22/wg14/www/C99RationaleV5.10.pdf

Ответ 5

$5.7/6 - "Если оба указателя не указывают к элементам одного и того же объекта массива, или один за последним элементом массив, поведение undefined.75)"

Сводка: undefined , даже если вы не разыщите указатель.

Ответ 6

Правильные ответы были даны много лет назад, но мне интересно, что C99 обоснование [сек. 6.5.6, последние 3 абзаца] объясняет, почему стандарт поддерживает добавление 1 к указателю, указывающему на последний элемент массива (p+1):

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

и почему p-1 не поддерживается:

В случае p-1, с другой стороны, перед массивом объектов, которые пересекаются p, должен быть выделен весь объект, так что падают декременты, которые выходят из нижней части массива. Это ограничение позволяет сегментированные архитектуры, например, размещать объекты в начале диапазона адресной памяти.

Итак, если указатель p указывает на объект в начале диапазона адресной памяти, который одобрен этим комментарием, тогда p-1 будет генерировать недоиспользование.

Обратите внимание, что целочисленное переполнение является стандартным примером поведения undefined [сек. 3.4.3], поскольку это зависит от среды перевода и операционной среды. Я считаю, что легко видеть, что эта зависимость от среды распространяется на нижний поток указателя.

Вот почему стандарт явно делает это поведение undefined [в 6.5.6/8], как отмечают другие ответы здесь. Чтобы привести это предложение:

Если оба операнда указателя и результат указывают на элементы одного и того же объекта массива или один за последним элементом объекта массива, оценка не должна приводить к переполнению; в противном случае поведение undefined.

См. также [сек. 6.3.2.3, последние 4 абзаца] обоснования C99, в котором дается более подробное описание того, как могут создаваться недействительные указатели и какие последствия могут иметь.

Ответ 7

Да, это поведение undefined. См. принятый ответ на этот близкий вопрос. Назначение недопустимого указателя на переменную, сравнение недопустимого указателя, запуск неверного указателя триггеров undefined.