type programming c language-lawyer bit-fields c11

programming - ¿Cuál es el tipo de campo de bits?



bit type c (7)

... el tipo real hace una diferencia en C11 con _Generic

Los resultados de cómo 1 compilador trata los tipos de campo de bits se muestran a continuación.

Los campos de 8 y 32 bits coinciden con los sospechosos habituales.

¿Cuál es el tipo de campo de bits de 1 bit? Como otros han citado, su "nombre" no está claramente especificado, pero no es ninguno de los tipos estándar esperados.

Esto no cita una especificación, pero demuestra cómo un compilador respetado interpretó la especificación C.

GNU C11 (GCC) versión 5.3.0 (i686-pc-cygwin)
compilado por GNU C versión 5.3.0, GMP versión 6.1.0, MPFR versión 3.1.4, MPC versión 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); }

Salida

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

Nota con -Wconversion y el código de abajo, recibo esta advertencia. Entonces, al menos eso es lo que un compilador llama a su tipo de campo de bit pequeño: unsigned char:3 .

advertencia: la conversión a ''unsigned char: 3'' desde ''unsigned int'' puede alterar su valor [-Wconversion]

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

No puedo encontrar en ninguna parte del estándar C donde se especifique esto. Por ejemplo, en

struct { signed int x:1; } foo;

es foo.x un foo.x de tipo int , o algo más? Parece antinatural que sea un lvalue de tipo int ya que no puede almacenar ningún valor de tipo int en él, solo 0 o -1, pero no puedo encontrar ningún idioma que pueda asignarle un tipo diferente. Por supuesto, usado en la mayoría de las expresiones, se promocionaría a int todos modos, pero el tipo real hace una diferencia en C11 con _Generic , y tampoco puedo encontrar ningún lenguaje en el estándar sobre cómo los _Generic bits interactúan con _Generic .


Como Jonathan ya citó, p5 establece claramente qué tipo de campo de bit tiene.

Lo que también debe tener en cuenta es que hay una regla especial para las conversiones aritméticas de campos de bits en 6.3.1.1, que básicamente indica que si un int puede representar todos los valores, tal campo de bits se convierte en un int en la mayoría de las expresiones.

Lo que sería el tipo en un _Generic debería ser el tipo declarado (módulo el signo de fallo), ya que parece haber consenso en que las conversiones aritméticas no se aplican allí. Asi que

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

podría darle resultados diferentes si X es un campo de bits sin signo con un ancho que tiene todos los valores encajados en un int .


Dado que incluyó el calificador signed , entonces los únicos valores que se pueden almacenar en el campo de bit de 1 bit son de hecho -1 y 0. Si omitió el calificador, la implementación se definiría si el bit int ''plain'' El campo fue firmado o sin firmar. Si hubiera especificado unsigned int , por supuesto, los valores serían 0 y +1.

Las secciones relevantes de la norma son:

§6.7.2.1 Especificadores de estructura y unión

¶4 La expresión que especifica el ancho de un campo de bits será una expresión constante con un valor no negativo que no exceda el ancho de un objeto del tipo que se especificaría si se omitieran los dos puntos y la expresión. 122) Si el valor es cero, la declaración no tendrá declarador.

¶5 Un campo de bits debe tener un tipo que sea una versión calificada o no calificada de _Bool , signed int , unsigned int o algún otro tipo definido por la implementación. Se define por implementación si se permiten los tipos atómicos.

¶10 Un campo de bits se interpreta como que tiene un tipo entero con signo o sin signo que consiste en el número especificado de bits. 125) Si el valor 0 o 1 se almacena en un campo de bits de ancho cero distinto del tipo _Bool , el valor del campo de bits se comparará igual al valor almacenado; un _Bool bits _Bool tiene la semántica de un _Bool .

122) Mientras que el número de bits en un objeto _Bool es al menos CHAR_BIT , el ancho (número de bits de signo y valor) de un _Bool puede ser solo de 1 bit.

125) Como se especifica en 6.7.2 arriba, si el especificador de tipo real utilizado es int o un typedef-name definido como int , entonces está definido por la implementación si el campo de bits está firmado o sin firmar.

La nota de pie de página 125 apunta a:

§6.7.2 Especificadores de tipo

¶5 Cada uno de los multisets separados por comas designa el mismo tipo, excepto que para los campos de bits, está definido por la implementación si el especificador int designa el mismo tipo como signed int o el mismo tipo que unsigned int .


El estándar C11 establece en 6.7.2.1p5:

Un campo de bits debe tener un tipo que sea una versión calificada o no calificada de _Bool, int firmado, int firmada o algún otro tipo definido por la implementación.

Esta es una restricción, lo que significa que si un programa declara un campo de bits con un tipo que no se encuentra en una de las categorías anteriores, debe imprimirse un diagnóstico.

Sin embargo, luego continúa diciendo en 6.7.2.1p10:

Un campo de bits se interpreta como que tiene un tipo entero con signo o sin signo que consiste en el número especificado de bits.

Creo que lo que quieren decir es que, si bien debe declarar el campo de bits con algo como signed int x:n , el tipo de expresión foo.x es otro tipo de entero con signo, foo.x T. T es un tipo de entero con signo consiste en n bits y debe cumplir con las restricciones en todos los tipos de enteros con signo que se dan en la Sec. 6.2.6. Pero T no es necesariamente compatible con el tipo de entero con signo estándar denominado signed int . (De hecho, la única situación en la que es posible que T sea compatible con ese tipo estándar es si n sucede que es igual al número de bits en un int firmado).

Desafortunadamente, el Estándar no proporciona ningún medio para nombrar el tipo T, por lo que no puedo ver cómo se puede usar de forma _Generic , al menos, no de manera portátil. Las implementaciones específicas del lenguaje C pueden proporcionar un mecanismo para nombrar este tipo, pero el Estándar no las obliga a hacerlo.


El tipo de campo de bits es:

campo de bits de tipo T

donde T es _Bool , int , signed int , unsigned int o algún tipo definido por la implementación.

En su ejemplo, foo.x es de tipo: campo de bits de tipo signed int .

Esto es diferente de signed int porque los dos tipos no comparten las mismas restricciones y requisitos.

Por ejemplo:

/* 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;


La especificación C11 ciertamente no lo deja en claro, y quizás sea deficiente.

Creo que foo.x es un lvalue con un tipo diferente a int , pero mi justificación es bastante débil:

6.2.7 El párrafo 1 dice:

Dos tipos tienen tipo compatible si sus tipos son iguales.

6.3 el párrafo 2 dice:

La conversión de un valor de operando a un tipo compatible no causa ningún cambio en el valor o la representación.

Si foo.x es un lvalor de tipo int , entonces sería compatible con otros int s, por lo que foo.x = 5 debería dar como resultado que foo.x tenga un valor de 5 (por 6.3p2). Obviamente, eso no puede suceder, lo que sugiere que foo.x no es compatible con int , lo que sugiere que foo.x no es un lvalue de tipo int .

Realmente no tiene sentido que foo.x no sea compatible con int . Es posible que no se produzca ninguna conversión (en el sentido 6.3.1) y que foo.x obtenga su valor a través de algún mecanismo no descrito en la norma. O tal vez no entiendo lo que significa "operandos aritméticos", y ese 6.3.1 no se aplica a los valores l.

También hay 6.3.1.1, párrafo 1, viñeta 2, que dice:

  • El rango de un tipo entero con signo será mayor que el rango de cualquier tipo de entero con signo con menos precisión.

foo.x tiene menos precisión que un int ordinario (cuando se usa como un lvalue, no cuando se convierte al valor almacenado en el objeto designado como se describe en 6.3.2.1p2), por lo que debe tener un rango de conversión de entero diferente . Esto también sugiere que no es un int .

Pero no estoy seguro de que mi interpretación sea válida o coincida con la intención del comité.

Recomendaría enviar un informe de defectos sobre esto.


Yo sospecharía que esto dependería de:

  1. El compilador
  2. Ajustes de optimización
  3. El número máximo de bits en el campo de bits