Какой тип битового поля? - программирование

Какой тип битового поля?

Я не могу найти нигде в стандарте C, где это указано. Например, в

struct { signed int x:1; } foo;

есть foo.x lvalue типа int, или что-то еще? Кажется неестественным для него быть lvalue типа int, так как вы не можете хранить в нем какое-либо значение типа int, только 0 или -1, но я не могу найти какой-либо язык, который присвоил бы ему другой тип. Конечно, используется в большинстве выражений, в любом случае его можно было бы повысить до int, но фактический тип имеет значение в C11 с _Generic, и я не могу найти какой-либо язык в стандарте о том, как битовые поля взаимодействуют с _Generic либо.

4b9b3361

Ответ 1

Спецификация C11, конечно, не делает этого ясным и, возможно, является недостаточным.

Я считаю, что foo.x является lvalue с типом, отличным от int, но мое оправдание довольно слабое:

6.2.7 в пункте 1 говорится:

Два типа имеют совместимый тип, если их типы одинаковы.

6.3 параграф 2 гласит:

Преобразование значения операнда в совместимый тип не приводит к изменению значения или представления.

Если foo.x является lvalue типа int, тогда оно будет совместимо с другим int, поэтому foo.x = 5 должно привести к foo.x, имеющему значение 5 (на 6.3p2). Этого, очевидно, не может быть, предполагая, что foo.x несовместим с int, предполагая, что foo.x не является значением l type int.

На самом деле не имеет смысла, что foo.x не совместим с int. Возможно, никакого преобразования (в смысле 6.3.1) не происходит, и что foo.x получает свое значение через некоторый механизм, не обсуждаемый в стандарте. Или, может быть, я не понимаю, что означает "арифметические операнды", и что 6.3.1 не относится к lvalues.

Там также пункт 6.3.1.1 параграфа 1, который гласит:

  • Ранг знакового целочисленного типа должен быть больше ранга любого целочисленного типа со знаком с меньшей точностью.

foo.x имеет меньшую точность, чем обычный int (при использовании в качестве значения l, а не когда он "преобразуется в значение, хранящееся в указанном объекте", как описано в 6.3.2.1p2), поэтому он должен иметь другой целочисленный ранг преобразования. Это также говорит о том, что это не int.

Но я не уверен, что моя интерпретация верна или соответствует намерению комитета.

Я бы рекомендовал отправить отчет об ошибке.

Ответ 2

Учитывая, что вы включили квалификатор signed, то единственные значения, которые могут быть сохранены в 1-битовом битовом поле, действительно равны -1 и 0. Если бы вы опустили квалификатор, то была бы определена реализация, "plain" int битное поле было подписано или без знака. Если бы вы указали unsigned int, конечно, значения были бы 0 и +1.

Соответствующие разделы стандарта:

§6.7.2.1 Спецификации структуры и объединения

¶4 Выражение, определяющее ширину битового поля, должно быть целочисленной константой выражение с неотрицательным значением, которое не превышает ширину объекта тип, который будет указан, были двоеточие и выражение опущено. 122) Если значение равно ноль, декларация не имеет декларатора.

¶5 Битовое поле должно иметь тип, который является квалифицированной или неквалифицированной версией _Bool, signed int, unsigned int или другого определенного типа реализации. это реализация - определяется, разрешены ли атомарные типы.

¶10 Битовое поле интерпретируется как имеющее целое число со знаком или без знака, состоящее из указанное число бит. 125) Если значение 0 или 1 сохраняется в битовое поле с ненулевой шириной type _Bool, значение битового поля должно сравниваться с сохраненным значением; a _Boolбит-поле имеет семантику a _Bool.

122) Пока количество бит в объекте _Bool не меньше CHAR_BIT, ширина (количество знаков и биты значений) _Bool может быть всего 1 бит.

125) Как указано в пункте 6.7.2 выше, если используется фактический спецификатор типа int или typedef-name, определяемый как int, то он определяется реализацией, является ли бит-поле подписанным или неподписанным.

Сноска 125 указывает на:

§6.7.2. Спецификаторы типов

¶5 Каждый из мультимножеств, разделенных запятыми, обозначает один и тот же тип, за исключением того, что для битовых полей, это определяется реализацией, специфицирует ли спецификатор int тот же тип, что и signed int или того же типа, что и unsigned int.

Ответ 3

Как уже цитировал Джонатан, p5 четко заявляет, что тип битового поля имеет.

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

То, что тип будет в _Generic, должен быть объявленным типом (по модулю скрещивания знака), так как кажется консенсусом, что арифметические преобразования не применяются. Так

_Generic((X), int: toto, unsigned: tutu)
_Generic(+(X), int: toto, unsigned: tutu)

может дать вам разные результаты, если X - это неподписанное битовое поле с шириной, которая имеет все значения, вписывающиеся в int.

Ответ 4

Стандарт C11 указывает в 6.7.2.1p5:

Битовое поле должно иметь тип, который является квалифицированной или неквалифицированной версией _Bool, подписанным int, unsigned int или каким-либо другим определенным для реализации типом.

Это ограничение, означающее, что если программа объявляет поле бит с типом, который не относится к одной из вышеперечисленных категорий, должна быть напечатана диагностика.

Однако далее в 6.7.2.1p10:

Битовое поле интерпретируется как имеющее целое число со знаком или без знака, состоящее из указанного количества бит.

Я верю, что они означают, что, хотя вы должны объявить поле бит с чем-то вроде signed int x:n, тип выражения lvalue foo.x - это какой-то другой тип целочисленного знака, назовите его T. T - целое число со знаком тип, состоящий из n битов и должен соответствовать ограничениям на все знаковые целые типы, приведенные в разд. 6.2.6. Но T не обязательно совместим со стандартным знаковым целым типом с именем signed int. (Фактически, единственная ситуация, при которой T может быть совместима с этим стандартным типом, заключается в том, что n совпадает с числом бит в подписанном int.)

К сожалению, Стандарт не предоставляет никаких средств для обозначения типа T, поэтому я не вижу, как его можно использовать в _Generic, по крайней мере, не переносимым образом. Специфические реализации языка C могут предоставить механизм для обозначения этого типа, но стандарт не принуждает их.

Ответ 5

Тип битового поля:

бит-поле типа T

где T либо _Bool, int, signed int, unsigned int, либо определенный тип реализации.

В вашем примере foo.x имеет тип: бит-поле типа signed int.

Это отличается от signed int, потому что два типа не имеют одинаковых ограничений и требований.

Например:

/* foo.x is of type bit-field of type signed int */
struct { signed int x:1; } foo; 

/* y is of type signed int */
signed int y;                     

/* valid, taking the size of an expression of type signed int */
sizeof y;

/* not valid, taking the size of an expression of type bit-field
 * of signed int */
sizeof foo.x;  

/* valid, taking the address of a lvalue of type signed int */
&y;            

/* invalid, taking the address of a lvalue of type bit-field
 * of signed int */
&foo.x;        

Ответ 6

... фактический тип делает разницу в C11 с _Generic

Ниже показано, как 1 компилятор обрабатывает типы полей бит.

8-битные и 32-битные поля соответствуют обычным подозреваемым.

Каков тип 1-битного битового поля? Как процитировали другие, его "имя" четко не указано, но оно не относится к ожидаемым стандартным типам.

Это не указывает спецификацию, но демонстрирует, как уважаемый компилятор интерпретировал спецификацию C.


GNU C11 (GCC) версия 5.3.0 (i686-pc-cygwin)
скомпилированный GNU C версии 5.3.0, версия GMP 6.1.0, версия MPFR 3.1.4, версия MPC 1.0.3

#define info_typename(X) _Generic((X), \
  _Bool: "_Bool", \
  unsigned char: "unsigned char", \
  signed char: "signed char", \
  char: "char", \
  int: "int", \
  unsigned : "unsigned", \
  default: "default" \
  )

#define TYPE_NAME(t) { printf("%s\n", info_typename(t)); }
#include <stdio.h>

int main() {
  struct {
    signed int x1 :1;
    signed int x8 :8;
    signed int x32 :32;
    _Bool b;
    signed char sc;
    char c;
    unsigned char uc;
    int i;
    unsigned u;
  } foo;
  TYPE_NAME(foo.b);
  TYPE_NAME(foo.sc);
  TYPE_NAME(foo.c);
  TYPE_NAME(foo.uc);
  TYPE_NAME(foo.i);
  TYPE_NAME(foo.u);
  TYPE_NAME(foo.x1);
  TYPE_NAME(foo.x8);
  TYPE_NAME(foo.x32);
}

Выход

_Bool
signed char
char
unsigned char
int
unsigned
default           (int:1)
signed char       (int:8)
int               (int:32) 

Обратите внимание на -Wconversion и приведенный ниже код, я получаю это предупреждение. Таким образом, это, по крайней мере, то, что один из компиляторов называет своим типом битового поля: unsigned char:3.

предупреждение: преобразование в 'unsigned char: 3' из 'unsigned int' может изменить его значение [-Wconversion]

  struct {
    unsigned int x3 :3;
  } foo;
  unsigned u = 1;
  foo.x3 = u; // warning

Ответ 7

Я подозреваю, что это будет зависеть от:

  • Компилятор
  • Настройки оптимизации
  • Максимальное количество бит в поле бит