Какой тип битового поля?
Я не могу найти нигде в стандарте C, где это указано. Например, в
struct { signed int x:1; } foo;
есть foo.x
lvalue типа int
, или что-то еще? Кажется неестественным для него быть lvalue типа int
, так как вы не можете хранить в нем какое-либо значение типа int
, только 0 или -1, но я не могу найти какой-либо язык, который присвоил бы ему другой тип. Конечно, используется в большинстве выражений, в любом случае его можно было бы повысить до int
, но фактический тип имеет значение в C11 с _Generic
, и я не могу найти какой-либо язык в стандарте о том, как битовые поля взаимодействуют с _Generic
либо.
Ответы
Ответ 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
Я подозреваю, что это будет зависеть от:
- Компилятор
- Настройки оптимизации
- Максимальное количество бит в поле бит