Получить размер членов структуры битового поля - PullRequest
0 голосов
/ 08 марта 2019

Я определил структуру как:

struct mystruct {
    uint32_t onebyte   :  8;
    uint32_t twobytes  : 16;
    uint32_t threebits :  3;
};

Я знаю, что C определяет битовые поля, как минимум, такими же широкими, как указано, но компилятор может использовать больше памяти (например, 3 * 4 байта в этом случае). Однако установленная ширина является гарантированным минимумом, и если значение превышает соответствующий диапазон, приложение все равно может работать корректно случайно.

Чтобы выполнить некоторые отладочные утверждения, я хочу, чтобы мой код проверял, превышает ли значение допустимый диапазон, перед установкой значения элемента:

assert(someval < (1 << sizeofbitfieldmemberinbits(((mystruct*)NULL)->threebits)));

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

assert(someveal <= ((mystruct){.threebits = -1}).threebits);

В любом случае, есть ли способ определить гарантированный минимальный размер элемента битового поля структуры C в битах (или, по крайней мере, в байтах)?

Я ищу выражение типа sizeofbitfieldmemberinbits(), которое может быть вычислено компилятором во время сборки (например, ((mystruct){.threebits = -1}).threebits может быть оценено как 0x7).

Edit: Как отметил Джон Боллинджер, память, выделенная для элемента битового поля, может быть больше указанного числа битов, но элемент никогда не может содержать значение, большее (1 << #bits) - 1. Однако, когда я пытаюсь установить значение, которое выходит за пределы, оно будет неявно обрезано (во время выполнения). С утверждением я хочу проверить случаи, когда такое усечение происходит не только , но когда действительно .

Ответы [ 2 ]

2 голосов
/ 08 марта 2019

Я ищу выражение типа sizeofbitfieldmemberinbits(), которое может быть рассчитан компилятором во время сборки

Термин, который вы описываете в Стандарте, называется " константное выражение ":

Постоянное выражение может быть оценено во время перевода, а не время выполнения, и, соответственно, может использоваться в любом месте, где константа может будет.

(C2011, 6,6 / 2)

Вы продолжаете разъяснять цель, для которой вы хотите использовать такое постоянное выражение:

С утверждением я хочу проверить случаи, когда такое усечение не просто может произойти, но когда на самом деле делает .

Обратите внимание, однако, что

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

  2. На самом деле вам не нужно постоянное выражение для использования в регулярном утверждении, таком как вы демонстрируете (в отличие от статического утверждения). Выражение в регулярном утверждении вычисляется во время выполнения.

  3. С другой стороны, некоторые выражения, которые не удовлетворяют определению константного выражения в стандарте, могут все еще вычисляться во время перевода (компиляции) некоторыми реализациями.

Точки (2) и (3) удачны для вас, потому что битовые поля имеют типы второго класса, которые не могут быть выражены напрямую. Нет значений любого типа битового поля вне контекста объекта структуры хоста, и нет имени типа, с помощью которого можно выразить эффективный тип битового поля. И означает, что означает, что нет постоянного выражения, которое оценивает число бит или максимальное значение элемента битового поля, если только оно не включает в себя предварительное знание этого элемента, поскольку структуры (включая литералы структуры) не входят в число операндов. который может появиться в подходящем константном выражении:

Арифметическое константное выражение должно иметь арифметический тип и должно иметь только операнды, которые являются целочисленными константами, плавающими константами, константы перечисления, символьные константы, sizeof выражения, чьи Результатами являются целочисленные константы и выражения _Alignof. В ролях операторы в выражении арифметической константы должны преобразовывать только арифметические типы для арифметических типов, кроме как часть операнда для оператор sizeof или _Alignof.

( C2011 6,6 / 8 )


После всего этого, я думаю, вопрос действительно сводится к следующему:

Я не уверен, гарантированно ли это работает:

assert(someveal <= ((mystruct){.threebits = -1}).threebits);

Для неподписанных битовых полей, таких как mystruct.threebits, он гарантированно будет работать в C99 или новее. Более ранние версии C не имеют составных литералов или назначенных инициализаторов, однако, и некоторые реализации C, с которыми вы можете столкнуться даже сегодня, не соответствуют C99. В такой реализации вы могли бы вместо этого просто определить (может быть const, возможно static) экземпляр вашей структуры, в которой можно записать ограничения ...

static const struct mystruct mystruct_limits = { -1, -1, -1 };

... а затем сравнить с его членами:

assert(someveal <= mystruct_limits.threebits);

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

Обратите также внимание, что хотя const является желательным для этой цели, он не был стандартизирован до C99. Однако до C99 это было довольно распространенное расширение, и вы с гораздо меньшей вероятностью столкнетесь с компилятором C, который отклоняет const, чем с компилятором, который отклоняет составные литералы.

0 голосов
/ 08 марта 2019

существует ли способ определения гарантированного минимального размера элемента битового поля структуры C в битах

Я хочу проверить случаи, когда такое усечение не только происходит, но и происходит на самом деле.

assert() недостаточно, поскольку связанный с ним код не обязательно присутствует во время выполнения.

Проверка по максимальному значению поля. Это легко сделать, если поле имеет тип unsigned .

#include <inttypes.h>
#include <stdio.h>

int main(void) {
  struct mystruct {
    uint32_t onebyte :8;
    uint32_t twobytes :16;
    uint32_t threebits :3;
  };
  struct mystruct obj;

  for (int test = 0; test < 20; test++) {
    unsigned n = rand() % 10;

    // I want to check for cases where such truncation not just might occur,
    //  but when actually does.
    if (n > (struct mystruct) {.threebits = -1}.threebits) {
      printf("Truncation will occur, %u\n", n);
    }
    obj.threebits = n;
  }
  return obj.threebits;
}

выход

Truncation will occur, 9
Truncation will occur, 8
Truncation will occur, 9
Truncation will occur, 8
Truncation will occur, 9
...