Может ли связанный список XOR быть реализован на C ++, не вызывая неопределенного поведения?

связанный список XOR - это модифицированная версия нормального двусвязного список, в котором каждый узел хранит только один «указатель» вместо двух. Этот «указатель» состоит из XOR следующего и предыдущего указателей. Для перемещения по списку требуется два указателя - один для текущего узла и один для следующего или предыдущего узла. Чтобы перемещаться вперед, адрес предыдущего узла XORed с «указателем», хранящимся в текущем узле, отображая истинный «следующий» указатель.

Стандарт C ++ вызывает кучу операций над указателями и целыми числами, что приводит к неопределенному поведению - например, вы не можете гарантировать, что установка определенного бита в число не приведет к тому, что аппаратное обеспечение вызовет прерывание, поэтому в некоторых случаях результаты бит twiddling может быть неопределенным.

Мой вопрос следующий: существует ли реализация C ++ связанного с XOR списка, которая не приводит к неопределенному поведению?

15
nl ja de
@curiousguy Это хороший момент. Есть ли у вас рекомендация по-другому выразить это предложение?
добавлено автор templatetypedef, источник
Аналогичный (но не тот же) вопрос есть здесь: добавлено автор Mark Wilkins, источник
@templatetypedef std может определить некоторое поведение как UB. Реализация может иметь нежелательное, непредсказуемое поведение.
добавлено автор curiousguy, источник
UB не является «результатом» реализации.
добавлено автор curiousguy, источник

1 ответы

Мой вопрос следующий: существует ли реализация C ++ связанного списка XOR, которая не приводит к неопределенному поведению?

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

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

Однако uintptr_t является необязательным типом, и поэтому он не является полностью переносимым. Нет требования, чтобы реализация C ++ фактически имела целочисленный тип, способный представлять адрес. Если в реализации нет uintptr_t , тогда код разрешается компилировать с помощью диагностики, и в этом случае его поведение выходит за рамки стандарта. Не уверен, считаете ли вы, что нарушение «без UB» или нет. Я имею в виду, серьезно, компилятор, который позволяет использовать код, который использует неопределенные типы? ;-)

Чтобы избежать uintptr_t I подумайте , что вы можете сделать свое свертывание бит в массиве символов sizeof (node ​​*) unsigned. Указатели являются типами POD и поэтому могут быть скопированы, повернуты и искалечены при условии, что представление объекта будет восстановлено до исходного состояния, прежде чем оно будет использоваться в качестве указателя.

Также обратите внимание, что если ваша реализация на C ++ имеет сборщик мусора, то преобразование в целое число/xor/xor-back-again/convert-to-pointer не обязательно останавливает собираемый объект (потому что это приводит к «несанкционированному производному указатель"). Поэтому для переносимости вы должны также убедиться, что результирующие указатели действительны. Два способа сделать это:

  1. вызывают declare_reachable .
  2. Используйте реализацию с безопасным указателем безопасности (это определяется реализацией, если это так, и вы можете проверить его с помощью get_pointer_safety() с учетом того, что непринужденной реализации разрешено ложно утверждать, что это строго).

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

  • сохранить отдельный контейнер всех значений указателя

This is not guaranteed to work. An unsafely derived pointer is invalid even if it happens to be equal to a safely-derived pointer (3.7.4.3/4). I was surprised too.

16
добавлено
@GManNickG: реализация может иметь 128-битные указатели, но не целых типов больше, чем 64 бита, например.
добавлено автор Keith Thompson, источник
Кроме того, +1 для поиска и упоминания пункта 3.7.4.3 в пункте 4. Я этого никогда не знал.
добавлено автор Cornstalks, источник
@SteveJessop: Ах, извините, я смотрел параграф 3 3.7.4 и пропустил подразделы 3.7.4. Дурак я. Спасибо что подметил это!
добавлено автор Cornstalks, источник
«Незнакомый производный указатель недействителен, даже если он оказывается равным безопасно полученному указателю (3.7.4.3/4)« Я не думаю, что 3.7.4.3/4 является правильной ссылкой для этого, так как 3.7.4.3 «Любые функции распределения и/или освобождения, определенные в программе на С ++, включая версии по умолчанию в библиотеке, должны соответствовать семантике, указанной в пунктах 3.7.4.1 и 3.7.4.2», и 3.7.4.4 нет.
добавлено автор Cornstalks, источник
Если вы не будете осторожны, полученные указатели не будут безопасно получены. declare_reachable может решить это с некоторой осторожностью , (В C ++ 11)
добавлено автор Yakk - Adam Nevraumont, источник
@SteveJessop так, вам тоже нужно их распутать? :) Также обратите внимание, что это не только сбор мусора, но и обнаружение утечки.
добавлено автор Yakk - Adam Nevraumont, источник
@SteveJessop: Является ли uintptr_t действительно необязательным? Я думал, что только фиксированные/наименьшие/быстрые целые типы являются необязательными.
добавлено автор GManNickG, источник
@SteveJessop: Спасибо, что я мой поисковый движок. :)
добавлено автор GManNickG, источник
Ударьте меня на 18 секунд, +1.
добавлено автор Seth Carnegie, источник
@GManNickG: вот что говорит 18.4.1.
добавлено автор Steve Jessop, источник
@Yakk: Я не думаю, что вы required , чтобы уловить их доступность, но да, это утечка, если вы этого не сделаете. Знание, когда вы можете их уловить, - это проблема управления ресурсами, аналогичная знанию, когда нужно освобождать вещи, и я оставляю это как упражнение для вопросителя ;-)
добавлено автор Steve Jessop, источник
@Yakk: Я просто писал то же самое.
добавлено автор Steve Jessop, источник
@Cornstalks: Я имею в виду [basic.stc.dynamic.safety] , я использую N3337, который является редакционным изменением FDIS плюс. Если нумерация разделов не соответствует опубликованному стандарту, я, вероятно, собираюсь получить много жалоб, когда и когда я ссылаюсь на измененные разделы на SO!
добавлено автор Steve Jessop, источник
pro.cxx
pro.cxx
3 049 участник(ов)

C/C++ chat 0. Простые вопросы, лабы и о IDE — в чат новичков @supapro 1. Не хамим, не переходим на личности, не вбрасываем утверждения без доказательств 2. No Ads, offtop, flood Объявления о вакансиях и евенты - в лс @AlexFails https://t.me/ProCxx/259155

supapro.cxx
supapro.cxx
1 925 участник(ов)

Чат для тех, кто немного знает C++, простые вопросы по реализации, синтаксису и ide – сюда, а для другого есть: /Главный чат по серьезным вопросам — @ProCxx /Чат по обсуждению всего — @fludpac

C++ Russia
C++ Russia
384 участник(ов)

Сообщество разработчиков C++ в Telegram.

cxx.Дискуссионная
cxx.Дискуссионная
298 участник(ов)

это не двач, общайтесь вежливо; разговор на почти любые темы; Не согласны с баном? В лс @AlexFails, @ivario

C++ для маленьких и тупых
C++ для маленьких и тупых
105 участник(ов)

Лоу левел (по среднему IQ участников) чатик ExtremeCode @extremecode Флудилка @extremecode_rest