Это неопределенное поведение, чтобы сформировать указатель диапазона из адреса стека? - PullRequest
9 голосов
/ 02 февраля 2012

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

Но не определено ли поведение для формирования диапазона указателей из одной переменной стека, например:

char c = 'X';
char* begin = &c;
char* end = begin + 1;

for (; begin != end; ++begin) { /* do something */ }

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

Это неопределенное поведение?

Ответы [ 6 ]

14 голосов
/ 02 февраля 2012

Это разрешено, поведение определено, и оба значения begin и end являются безопасными значениями указателя .

В стандартном разделе C ++ 5.7 ([expr.add]) параграф 4:

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

При использовании C аналогичное предложение можно найти в стандартном разделе 6.5.6 раздела C99 / N1256, пункт 7.

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


Кроме того, в разделе 3.7.4.3 ([basic.stc.dynamic.safety]) "Безопасно полученные указатели" есть сноска:

Этот раздел не накладывает ограничений на разыменование указателей на память, не выделенную ::operator new. Это поддерживает способность многих реализаций C ++ использовать двоичные библиотеки и компоненты, написанные на других языках. В частности, это относится к двоичным файлам C, поскольку разыменование указателей на память, выделенную с помощью malloc, не ограничено.

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

4 голосов
/ 02 февраля 2012

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

3 голосов
/ 02 февраля 2012

Это не неопределенное поведение, если вы не разыменовали недействительный итератор * 1002. *.
Вам разрешено удерживать указатель на память за пределами вашего выделения, но нельзя разыменовывать ее.

2 голосов
/ 02 февраля 2012

5.7-5 ISO14882: 2011 (e) гласит:

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

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

редактирование: Действительно, когда вы добавляете 5.7-4, оказывается, что выполняемая вами операция (фактически) над массивом, таким образом, предложение неприменимо:

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

0 голосов
/ 02 февраля 2012

В общем случае было бы неопределенным поведение указывать за пределы области памяти, однако существует исключение для «один за концом», которое действует в соответствии со стандартом.&c+1 является допустимым указателем, но не может быть безопасно разыменован.

0 голосов
/ 02 февраля 2012

Вы можете определить c как массив размером 1:

char c[1] = { 'X' };

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

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