Проблема с соединением и упаковкой структуры
Вопрос
Я пишу некоторое программное обеспечение, где каждый бит должен быть точным (это для процессора), поэтому __packed очень важен.
typedef union{
uint32_t raw;
struct{
unsigned int present:1;
unsigned int rw:1;
unsigned int user:1;
unsigned int dirty:1;
unsigned int free:7;
unsigned int frame:20;
} __packed;
}__packed page_union_t;
это моя структура и союз.Однако это не работает:
page_union_t p; //.....
//This:
p.frame=trg_page;
p.user=user;
p.rw=rw;
p.present=present;
//and this:
p.raw=trg_page<<12 | user<<2 | rw<<1 | present;
должен создать тот же uint32.Но они не создают одно и то же.
Есть ли что-то, чего я не вижу, что не так с моим профсоюзом?
Решение
Ваша структура содержит всего 31 бит
Другие советы
AFAIK, порядок, в котором хранятся биты в структуре, не определен стандартом C99 (и стандартом C89 тоже).Скорее всего, биты расположены в порядке, обратном тому, который вы ожидали.
Вы должны были показать полученный результат так же, как результат, которого вы ожидали, - это помогло бы нам с диагностикой.Используемый вами компилятор и платформа, на которой вы работаете, также могут иметь большое значение.
В macOS X 10.4.11 (PowerPC G4) этот код:
#include <inttypes.h>
#include <stdio.h>
typedef union
{
uint32_t raw;
struct
{
unsigned int present:1;
unsigned int rw:1;
unsigned int user:1;
unsigned int dirty:1;
unsigned int free:7;
unsigned int frame:20;
};
} page_union_t;
int main(void)
{
page_union_t p = { .raw = 0 }; //.....
unsigned trg_page = 0xA5A5A;
unsigned user = 1;
unsigned rw = 1;
unsigned present = 1;
p.frame = trg_page;
p.user = user;
p.rw = rw;
p.present = present;
printf("p.raw = 0x%08X\n", p.raw);
p.raw = trg_page<<12 | user<<2 | rw<<1 | present;
printf("p.raw = 0x%08X\n", p.raw);
p.raw <<= 1;
printf("p.raw = 0x%08X\n", p.raw);
return(0);
}
выдает показанные результаты:
p.raw = 0xE014B4B4
p.raw = 0xA5A5A007
p.raw = 0x4B4B400E
При обратном порядке расположения полей результат становится более близким к объяснению:
#include <inttypes.h>
#include <stdio.h>
typedef union
{
uint32_t raw;
struct
{
unsigned int frame:20;
unsigned int free:7;
unsigned int dirty:1;
unsigned int user:1;
unsigned int rw:1;
unsigned int present:1;
};
} page_union_t;
int main(void)
{
page_union_t p = { .raw = 0 }; //.....
unsigned trg_page = 0xA5A5A;
unsigned user = 1;
unsigned rw = 1;
unsigned present = 1;
p.frame = trg_page;
p.user = user;
p.rw = rw;
p.present = present;
printf("p.raw = 0x%08X\n", p.raw);
p.raw = trg_page<<12 | user<<2 | rw<<1 | present;
printf("p.raw = 0x%08X\n", p.raw);
p.raw <<= 1;
printf("p.raw = 0x%08X\n", p.raw);
return(0);
}
Это дает результат:
p.raw = 0xA5A5A00E
p.raw = 0xA5A5A007
p.raw = 0x4B4B400E
Первый результат содержит E в качестве последней шестнадцатеричной цифры, поскольку младший значащий бит не используется, поскольку в структуре битового поля определен только 31 бит..
Если точное расположение битов имеет значение, ваш самый безопасный выбор - явная упаковка и распаковка структуры в массив без знака char.Все остальное слишком зависит от реализации.
Для справки всем, кто может найти это, попробуйте использовать атрибут packed:
struct __attribute__((packed)){
}
Вы не упоминаете, что заранее очищаете фрагменты структуры, вы уверены, что в первом случае у вас не останутся фрагменты мусора?
// maybe try this
page_union_t p = {0};