Внутреннее устройство союзов C++

Я пытаюсь узнать больше о C++ и столкнулся с кодом в шахматной программе, и мне нужна помощь в понимании. У меня есть союз, например:

union b_union {

    Bitboard b;
    struct {
    #if defined (BIGENDIAN)
        uint32_t h;
        uint32_t l;
    #else
        uint32_t l;
        uint32_t h;
    #endif
    } dw;
};

Приведенный выше код попадает в условие else.

Bitboard определяется как uint64_t. Если у меня есть значение, скажем, 0x0025f780, то есть 282578800148862, и я устанавливаю union.b = 0x0025f780, тогда union.dw.l обновляется до 16843134, а union.dw.h обновляется до 65793. Первоначально l и h начинаются с 3435973836. Что произошло внутри? Я довольно новичок в C++. Просто пытаюсь понять профсоюзы, как они работают внутри.

Большое спасибо за любые идеи.

Дэйвид


person David Whitten    schedule 01.11.2011    source источник


Ответы (4)


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

Другое очень распространенное использование объединений — доступ к частям другого элемента (что, кстати, является неопределенным поведением). В вашем случае два представления 64-битного целого числа. Один представляет собой целое число, а другой — две половины, как отдельные 32-битные объекты.

Обратите внимание, что разные компьютеры хранят 64-битное значение по-разному. Некоторые хранят байты от более значимого к менее значимому (обратный порядок байтов), некоторые наоборот (маленький порядок байтов), а некоторые используют смешанную форму (смешанный порядок байтов). Названия, кстати, взяты из «Путешествий Гулливера», где кто-то ел яйцо с большой стороны, а кто-то с заостренной.

В вашем случае я бы посоветовал вам полностью отказаться от союза и получить доступ к частям, используя:

low = uint32_t(b);
high = uint32_t(b >> 32);

Вышеупомянутое будет работать на всех архитектурах и так же быстро или даже быстрее, чем объединение.

person Lindydancer    schedule 01.11.2011
comment
Сладкий. На самом деле я понял, как получить кайф на ›› 32, но не был уверен в части l. Вы сэкономили мне много времени. Большое спасибо. - person David Whitten; 02.11.2011

Объединение означает, что компоненты будут занимать одну и ту же ячейку памяти. В примере кода, который вы показали, цель состоит в том, чтобы позволить вам напрямую ссылаться на старшие и младшие 32 бита b.

Обратите внимание, что этот код вызывает неопределенное (или определенное реализацией) поведение. Это связано с тем, что вы получаете доступ к элементу объединения из другого элемента, в который записываются данные.

Таким образом, b, представляющее собой 64-битное целое число, будет использовать ту же ячейку памяти, что и l и h, которые относятся к младшим и старшим 32-битам. Конечно, действительность этого зависит от порядка байтов машины, поэтому существует препроцессор if-else.

РЕДАКТИРОВАТЬ: ваш конкретный пример также неверен. Но вот исправленная версия:

Когда вы устанавливаете b = 282578800148862, (b = 0x101010101017e). Верхние и младшие 32 бита:

00010101 0101017e

so

l = 0x0101017e = 16843134
h = 0x00010101 = 65793
person Mysticial    schedule 01.11.2011
comment
Хм, я действительно не знал, что обозначают l и h. Спасибо, что указали на это. Я поиграю с этим еще немного. Это шахматный код, и я потратил неделю или около того, пытаясь освоить волшебные битборды. Довольно сложные вещи. - person David Whitten; 01.11.2011

Объединения объявляются только с одним значением за раз. Он может «объявлять» несколько значений, но хранить только одно за раз, а предыдущее перезаписывается. В вашем случае union.b установил значение, но присвоил его другим переменным. Вы не можете хранить значение BitBoard и значение структуры, оно должно быть либо тем, либо другим. Поэтому, когда вы отправились проверять, вы уже перезаписали свои старые значения. Я думаю, что структура лучше подходит для этого сценария, но вы всегда можете попробовать выполнить код, если вы не уверены. Здесь ваши значения l и h начали сливаться с битовой доской, что вызвало проблемы.

person Marc DiMillo    schedule 01.11.2011

В этом случае вам лучше иметь дело с шестнадцатеричными числами.

Что происходит, так это то, что union dw и uint64_t b занимают одно и то же место в памяти. l и h представляют младшие и старшие 32-битные части b.

В с обратным порядком байтов старшая 32-битная часть также является старшим битом, когда значение находится в Память. В Little-Endian все с точностью до наоборот. Вот почему у вас есть #ifdef.

Это делает l младшими 32 битами b (0xf780), а h — старшими 32 битами b (0x0025).

Фактические значения, которые вы упомянули, не имеют особого смысла, и у вас, вероятно, есть какая-то другая проблема. 282578800148862 не 0x0025f780.

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

person littleadv    schedule 01.11.2011