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

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

struct { signed int x:1; } foo; 

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

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

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

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

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

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

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

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

§6.7.2.1 Специфика структуры и союза

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Но я не уверен, что мое толкование действительно или соответствует намерению комитета.

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

Стандарт C11 гласит в 6.7.2.1p5:

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

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

Однако далее в 6.7.2.1p10 говорится:

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

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

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

… фактический тип делает разницу в 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  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 

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

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

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

битовое поле типа 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;