массивы + объединения + структуры, содержащие битовые поля C++

Я просто играл с битовыми полями и наткнулся на то, что не могу понять, как обойти.

(Примечание о платформе: размер int = 2 байта, long = 4 байта, long long = 8 байтов - думаю, стоит упомянуть, поскольку я знаю, что он может варьироваться. Также тип «байт» определяется как «беззнаковый символ»)

Я хотел бы иметь возможность сделать массив из двух 36-битных переменных и поместить их в объединение с массивом из 9 байтов. Вот что я придумал:

typedef union {
  byte bytes[9];
  struct {
    unsigned long long data:36;
  } integers[2];
} Colour;

Я работал над теорией, согласно которой компилятор поймет, что в анонимной структуре должно быть два битовых поля, и поместит их вместе в пространство размером 9 байт. Однако оказывается, что они выравниваются по границе байта, поэтому объединение занимает 10 байтов, а не 9, что вполне логично.

Вопрос в том, есть ли способ создать такой массив из двух битовых полей? Я рассматривал атрибут "packed", но компилятор его просто игнорирует.

Хотя это работает, как и ожидалось (sizeof() возвращает 9):

typedef union {
  byte bytes[9];
  struct {
    unsigned long long data0:36;
    unsigned long long data1:36;
  } integers;
} Colour;

Было бы предпочтительнее, чтобы он был доступен в виде массива.


Редактировать: спасибо cdhowie за его объяснение, почему это не сработает.

К счастью, я придумал способ добиться того, чего хочу:

typedef union {
  byte bytes[9];
  struct {
    unsigned long long data0:36;
    unsigned long long data1:36;
    unsigned long long data(byte which){
      return (which?data1:data0);
    }
    void data(byte which, unsigned long long _data){
      if(which){
        data1 = _data;
      } else {
        data0 = _data;
      }
    }
  } integers; 

} Colour; 

person Tom Carpenter    schedule 17.10.2012    source источник


Ответы (1)


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

Указатели должны быть выровнены по границам байтов, это просто указатели. Поскольку в большинстве случаев (за исключением) массивы функционируют как указатели, это просто невозможно с битовыми полями, которые содержат количество битов, не кратное 8 без остатка. (Что вы ожидаете от &(((Colour *) 0)->integers[1]), если битовые поля были упакованы? смысл?)

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

Вы заметите, что если вы попытаетесь взять адрес (((Colour *) 0)->integers.data0) или data1 во втором примере, компилятор выдаст ошибку именно по этой причине.

person cdhowie    schedule 17.10.2012
comment
Спасибо за это объяснение, имеет смысл. Меня только что осенило, что есть способ получить эффект массива, не имея его на самом деле: typedef union { byte bytes[9]; struct { unsigned long long data0:36; беззнаковые длинные длинные данные1:36; unsigned long long data(byte which){ return (what?data1:data0); } } целые числа; } Цвет; - person Tom Carpenter; 17.10.2012
comment
@TomCarpenter Да, это сработает. Пока вам не нужно перевернуть некоторые биты. ;) - person cdhowie; 17.10.2012
comment
К счастью, мне нужно, чтобы массив был таким: bytes[] используется для ввода данных в объединение, а затем integers.data[] (ну, на самом деле, integers.data() сейчас) используется для его переформатирования. - person Tom Carpenter; 17.10.2012
comment
Я полагаю, что также можно добавить функцию установки, на тот случай, если в какой-то момент мне нужно будет пойти другим путем. - person Tom Carpenter; 17.10.2012