Чтение младшего бита указателя таким образом, чтобы *вероятно* работало на как можно большем количестве систем.

Кажется, что младший бит указателей, равный 0, является более или менее довольно переносимым (где переносимость, очевидно, не означает «стандартный», но людям это сходит с рук, и в некоторых случаях они могут использовать это с некоторым преимуществом, надеюсь, можно отключить с помощью переключателя компиляции).

Проекты, которые хотят быть неудобными, использовали его, с меньшим успехом на втором младшем бите:

Насколько переносимым является использование низкого немного указателя в качестве флага?

Но скажем, не хочется просто совать бит данных или нет в указатель известного типа. Вместо этого вы хотели бы использовать этот младший бит, равный 0, чтобы позволить типу указателя выполнять «двойную функцию» в качестве терминатора.

Таким образом, ваши элементы выглядят так:

struct Item {
    uintptr_t flags; // low bit zero means "not an item"
    type1 field1;
    type2 field2;
    ...
};

Затем вы хотели бы иметь ситуацию, когда некоторый контейнер элементов выглядит следующим образом:

[(flags field1 field2...) (flags field1 field2...) some-pointer stuff stuff...]

Таким образом, вам сойдет с рук «невозвратная стоимость» (скажем, какой-то внутренний указатель управления в структуре данных для другой цели), который сделает ваше завершение за вас.


ОБНОВЛЕНИЕ: Чтобы прояснить ситуацию: здесь можно контролировать кодовую базу и структуры. Таким образом, любой указатель в структуре, используемой таким образом, вы можете объявить как тип объединения, например:

union Maybe_Terminator_Pointer {
    uintptr_t flags;
    type1* pointer1;
    type2* pointer2;
    ...
};

... и затем используйте это, если это поможет. Исключение char* — это нормально, так как они, конечно, не будут учитываться.


Таким образом, дополнительная проблема с каламбуром здесь заключается в следующем: указатель, используемый для выполнения теста на завершение, представляет собой Item*, а подпрограмма, выполняющая проверку, не знает, какой конкретно тип указателя some-pointer.

Мне интересно, какова лучшая игра, если таковая имеется, для возможности переноса и компиляции такого трюка. Это включает в себя преобразование указателей в союзы, #ifdef'ing endianness машины и получение char* из байта с битом и т. д. Все, что может больше сработать, если у кого-то есть опыт или догадки.

Представьте, что в вашем случае стоит затраченных усилий, избавившись от большого объема данных. И у вас есть резервный сценарий, если люди, компилирующие, обнаружат, что трюк где-то не работает... #ifdef может использовать полноразмерные элементы для терминаторов и тратить дополнительное пространство. Поэтому интересно, есть ли какие-нибудь советы, как сделать этот явно нарушающий стандарты трюк более эффективным на большем количестве систем.


person HostileFork says dont trust SE    schedule 10.01.2016    source источник
comment
Наименее непортативного способа нет. А архитектуры, которые не требуют выравнивания или если вы использовали packed structs, LSB вообще недоступны. Если у вас нет реальных ограничений памяти, дополнительный код не стоит хлопот. И если у вас есть проблемы с памятью, у вас все равно есть конкретная система. Тем не менее, это далеко за пределами стандарта C.   -  person too honest for this site    schedule 10.01.2016
comment
@Olaf Я не знаю, говорите ли вы, в частности, что этот случай значительно менее потенциально возможен, чем в связанном посте, или нет ... например. цитата В «теории»: насколько я знаю, это неопределенное поведение. В «реальности»: это будет работать на обычных машинах x86/x64 и, возможно, на ARM тоже? Мой вопрос больше касается аспекта реальности; тем более, что это можно (как я уже сказал) просто отключить при компиляции, если особенности среды не работают, так как быть на наименее плохой стороне компилятора при написании этого (лучше всего шансы, по сути)   -  person HostileFork says dont trust SE    schedule 10.01.2016
comment
Я указал сценарии, в которых он не работает, в том числе x86 и ARM. На 8-битных архитектурах это эпически провалится.   -  person too honest for this site    schedule 10.01.2016
comment
Не больше, чем на широкоформатных архитектурах. Но это совершенно не связанная тема.   -  person too honest for this site    schedule 10.01.2016
comment
Цель, позволяющая злоупотреблять указателями любого типа, крайне нереалистична. Большинство архитектур используют, например, все биты указателя для char указателей. Так что, по крайней мере, вы сможете сделать это только для указателей на типы данных, адреса которых гарантируют 0 младших битов, и это не будет переносимым.   -  person Tom Karzes    schedule 10.01.2016
comment
@TomKarzes Ах, да, char *, отредактировал это как необязательное. Также не требуется, чтобы действительно был какой-либо тип указателя, поэтому я тоже отредактировал это... есть только небольшое количество структур, которые будут делать это, но более одной... поэтому элемент не знает, какой конкретный тип другого написанного указателя, он бы попал. Очевидно, что он не является переносимым в том смысле, что не является стандартным, но я пояснил, что просто хочу посмотреть, есть ли метод, который эмпирически может работать на большем количестве платформ, если предложить эту функцию в качестве варианта сборки, которую люди могли бы попробовать.   -  person HostileFork says dont trust SE    schedule 10.01.2016
comment
Предполагая, что вы используете только тип с указанием, который имеет определенное требование к выравниванию, это переносимо, iff система использует естественное сопоставление uintptr_t с указателями (что и предполагается)   -  person M.M    schedule 19.01.2016


Ответы (1)


(Самостоятельный ответ, чтобы предоставить больше информации и позволить людям выявить любые потенциальные проблемы с моей альтернативой.)

Поэтому интересно, есть ли какие-нибудь советы, как сделать этот явно нарушающий стандарты трюк более эффективным на большем количестве систем.


Совет один (согласно комментариям): не делайте этого, если вы можете найти другой способ.

Например, «вы» упоминаете этот макет:

[(flags field1 field2...) (flags field1 field2...) some-pointer stuff stuff...]

Но есть ли в "stuff stuff" что-нибудь, что не является указателем - возможно, скучными старыми целыми числами, которые, как известно, четны - где вы могли бы проделать тот же трюк? Если да, то почему бы не переупорядочить это так:

[(flags field1 field2...) (flags field1 field2...) even-uintptr_t stuff...]

Таким образом, когда вы читаете flags из Item или нет, это будет тот же тип. Если вы посмотрите вокруг на вещи, которые не являются непрозрачными, такие как указатели, вы можете найти очевидные непрозрачные числа в текущем коде, которые всегда четны... например, множество подсчетов байтов, измеряющих агрегаты, - это то, что вы, вероятно, гарантированно есть как % 2 = 0.


Второй совет применим к вышеописанной альтернативе и, возможно, также помогает справиться с версией указателя, нарушающего стандарты. Убедитесь, что когда вы записываете значение, вы проходите через указатель «алиасинга», а не записываете поле напрямую через . или ->.

От компилятора не требуется обеспечивать согласованность памяти между двумя разными структурами в поле только потому, что они одного типа. Скажем, struct A начинается с uintptr_t field_a, а struct B начинается с uintptr_t field_b, и вы помещаете указатель на оба по одному и тому же адресу. Если вы сделаете some_a->field_a = value;, то при обратном чтении из указателя some_b->field_b по этому адресу вполне может не появиться это обновление, потому что компилятор не ожидает, что вы будете записывать поля B через указатель A.

Следовательно, пройдите через указатель, чтобы сделать запись. Что-то вроде uintptr_t *alias = &some_a->field_a;, а затем *alias = value обеспечит согласованность с последовательным чтением любого целого числа (!). (Неудовлетворенность последствиями этого свойства указателей для производительности вот почему restrict существует. Если это трюк должен работать, он может сделать это только за счет использования неограниченного поведения указателей.)

(!) - Я думаю, вам нужно только писать через указатель, а не читать, но, возможно, кто-то может дать представление.

person HostileFork says dont trust SE    schedule 18.01.2016