Является ли этот метод маркировки указателя в стандартном стандарте C?

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

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

Подумав об этом еще немного, я думаю, что у меня есть решение, которое работает для конкретного случая, описанного в исходном вопросе (все объекты одного размера, все объекты выделены из одного массива "кучи" ). Может ли кто-то подтвердить мои рассуждения?

// given:
typedef Cell ...; // such that sizeof(Cell) == 8
Cell heap[1024];  // or dynamic, w/e

// then:
void * tagged = ((char *)&heap[42]) + 3;  // pointer with tag 3 (w/e that means)

int tag = ((char *)tagged - (char *)heap) % sizeof(Cell);  // 3
Cell * ptr = (Cell *)((char *)tagged - tag);               // &heap[42]

В словах: не делается никаких предположений о целочисленном представлении указателя. Теги применяются путем индексирования байтов в указанном объекте. (Это, безусловно, разрешено.)

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

Является ли это все совместимым и, следовательно, переносным методом маркировки указателя? Является ли вычитание указателя по-прежнему разрешено работать, если тип массива преобразуется в что-то еще, char в этом случае? Могу ли я использовать sizeof(Cell) таким образом?

(Нет, я не знаю, почему эта техничность так много наводит на мой взгляд, да, переносимость легко достигается другими средствами.)

Ответ 1

Единственное, на что я думаю, что вам нужно быть более осторожным, вот ваши целые типы. Не используйте int:

  • Правильный тип различий указателей - ptrdiff_t.
  • результатом sizeof является size_t неподписанный тип
  • Вы выполняете % между size_t type и ptrdiff_t, поэтому результат, скорее всего, size_t, поэтому значение без знака
  • преобразование, что size_t в int - это реализация, определенная (поэтому не переносная), обычно она просто отбрасывает бит высокого порядка

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