Хранение неверного указателя автоматически неопределенное поведение? - PullRequest
18 голосов
/ 01 октября 2010

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

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

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

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

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

Ответы [ 7 ]

14 голосов
/ 01 октября 2010

У меня здесь есть C Craft Standard, и он делает его неопределенным из-за упущения.Он определяет случай ptr + I в 6.5.6 / 8 для

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

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

10 голосов
/ 01 октября 2010

Ваш код имеет неопределенное поведение по другой причине:

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

6 голосов
/ 01 октября 2010

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

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

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

3 голосов
/ 01 октября 2010

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

1 голос
/ 21 сентября 2016

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

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

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

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

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

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

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

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

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

1 голос
/ 01 октября 2010

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

Резюме, оно не определено , даже если вы не разыменовываете указатель .

0 голосов
/ 01 октября 2010

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

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