В самом строгом смысле это не неопределенное поведение, а определяемое реализацией. Таким образом, хотя это нежелательно, если вы планируете поддерживать неосновные архитектуры, вы можете , вероятно, сделать это.
Стандартная цитата, заданная interjay, является хорошей, указывая на UB, но это тольковторой лучший хит на мой взгляд, так как он имеет дело с арифметикой указатель-указатель (как ни странно, один явно UB, а другой нет). В этом вопросе есть параграф, касающийся непосредственно операции:
[expr.post.incr] / [expr.pre.incr]
Операндом должен быть [...] илиуказатель на полностью определенный тип объекта.
О, подождите, полностью определенный тип объекта? Вот и все? Я имею в виду, действительно, тип ? Значит, вам вообще не нужен объект?
Чтобы найти подсказку о том, что что-то там, возможно, не так четко определено, требуется совсем немного чтения. Поскольку до сих пор он читается так, как будто вам совершенно разрешено это делать, никаких ограничений.
[basic.compound] 3
делает утверждение о том, какой тип указателя может быть, и, будучи ни одним из трех других, результатвашей операции явно упадет до 3,4: неверный указатель .
Однако это не говорит о том, что вам не разрешено иметь неверный указатель. Напротив, в нем перечислены некоторые очень распространенные, нормальные условия (например, время окончания хранения), когда указатели регулярно становятся недействительными. Так что, по-видимому, это допустимо. И действительно:
[basic.stc] 4
Переадресация через недопустимое значение указателя и передача недопустимого значения указателя в функцию освобождения имеет неопределенное поведение. Любое другое использование недопустимого значения указателя имеет поведение, определяемое реализацией.
Мы делаем «любое другое» там, так что это не неопределенное поведение, а определяемое реализацией, поэтому обычно допустимо (если реализация явно не говорит что-то другое).
К сожалению, это еще не конец истории. Хотя с этого момента чистый результат больше не меняется, он становится все более запутанным, чем дольше вы будете искать «указатель»:
[basic.compound]
Допустимое значениеТип указателя объекта представляет собой либо адрес байта в памяти , либо нулевой указатель. Если объект типа T находится по адресу, который, как говорят, [...] указывает на этот объект, независимо от того, как было получено значение .
[Примечание: Например, адрессчитается, что один конец конца массива указывает на неродственный объект типа элемента массива, который может быть расположен по этому адресу. [...]].
Читайте как: ОК, кого это волнует! Пока указатель указывает где-то в памяти , я в порядке?
[basic.stc.dynamic.safety] Значение указателя является безопасно полученным указателем [blahбла]
Читайте как: ОК, безопасно выведено, что угодно. Это не объясняет, что это такое, и не говорит, что мне это действительно нужно. Безопасно-производные в-щеколду. Очевидно, у меня все еще могут быть указатели, не являющиеся безопасными, просто отлично. Я предполагаю, что разыменование их, вероятно, не будет хорошей идеей, но вполне допустимо иметь их. Иначе не сказано.
Реализация может иметь ослабленную безопасность указателя, и в этом случае достоверность значения указателя не зависит от того, является ли оно безопасным образом полученным значением указателя.
О, так что это может не иметь значения, только то, что я думал. Но подождите ... "не может"? Это означает, что может также . Откуда я знаю?
В качестве альтернативы, реализация может иметь строгую безопасность указателя, и в этом случае значение указателя, которое не является безопасно полученным значением указателя, является недопустимым значением указателя, если только ссылка на полный объект не являетсядинамического хранения и ранее был объявлен достижимым
Подождите, так что даже возможно, что мне нужно вызвать declare_reachable()
для каждого указателя? Откуда я знаю?
Теперь вы можете преобразовать в intptr_t
, который четко определен, давая целочисленное представление безопасно полученного указателя. Для которого, конечно, являясь целым числом, вполне законно и четко определено увеличивать его по своему усмотрению.
И да, вы можете преобразовать intptr_t
обратно в указатель, который также является четко определенным. Просто, не будучи исходным значением, больше не гарантируется, что у вас есть безопасный производный указатель (очевидно). Тем не менее, в целом, к букве стандарта, будучи определяемой реализацией, это на 100% законно:
[expr.reinterpret.cast] 5
Aзначение целочисленного типа или типа перечисления может быть явно преобразовано в указатель. Указатель преобразуется в целое число достаточного размера [...] и обратно в исходное значение того же типа указателя [...];в противном случае сопоставления между указателями и целыми числами определяются реализацией.
Улов
Указатели - это просто обычные целые числа, только вы случайно используете их в качестве указателей. О, если бы только это было правдой!
К сожалению, существуют архитектуры, в которых это не является истинным вообще, и просто генерирует недопустимый указатель (не разыменовывая его, просто имея его в регистре указателей),вызвать ловушку.
Так что это основа "реализация определена". Это и тот факт, что увеличение указателя в любое время, как вам угодно , может , конечно, вызвать переполнение, с которым стандарт не хочет иметь дело. Конечное адресное пространство приложения может не совпадать с местом переполнения, и вы даже не знаете, существует ли такая вещь, как переполнение для указателей в конкретной архитектуре. В общем, это кошмарный беспорядок, не имеющий никакого отношения к возможным преимуществам.
Работать с условием «один объект мимо» с другой стороны легко: реализация должна просто гарантировать, что ни один объект никогда не будеттаким образом, последний байт в адресном пространстве занят. Так что это четко определено, поскольку полезно и тривиально гарантировать.