c++ - ¿Por qué asignar un valor a un campo de bit no está devolviendo el mismo valor?
bit-fields signed-integer (6)
No puedo entender, cómo es posible que configuremos algo y luego no se muestre tal como está.
¿Estás preguntando por qué compila vs. te da un error?
Sí, idealmente debería darte un error.
Y lo hace, si usas las advertencias de tu compilador.
En GCC, con
-Werror -Wall -pedantic
:
main.cpp: In function ''int main()'':
main.cpp:7:15: error: overflow in conversion from ''int'' to ''signed char:1''
changes value from ''1'' to ''-1'' [-Werror=overflow]
s.enabled = 1;
^
El razonamiento de por qué esto se deja para ser definido por la implementación frente a un error puede tener más que ver con los usos históricos, donde requerir un lanzamiento significaría romper el código antiguo. Los autores de la norma pueden creer que las advertencias fueron suficientes para compensar a los interesados.
Para agregar algo de prescriptivismo, repetiré la declaración de @Lundin: "Nunca uses campos de bits para ningún propósito". Si tiene el tipo de buenas razones para obtener detalles de diseño de memoria de bajo nivel y específicos que lo lleven a pensar que necesitaba los campos de bits en primer lugar, los otros requisitos asociados que probablemente tenga se encontrarán con su especificación insuficiente.
(TL; DR: si eres lo suficientemente sofisticado como para legítimamente "necesitar" campos de bits, no están lo suficientemente definidos para servirte).
Vi el siguiente código en esta publicación de Quora :
#include <stdio.h>
struct mystruct { int enabled:1; };
int main()
{
struct mystruct s;
s.enabled = 1;
if(s.enabled == 1)
printf("Is enabled/n"); // --> we think this to be printed
else
printf("Is disabled !!/n");
}
Tanto en C como en C ++, la salida del código es inesperada ,
Está desactivado !!
Aunque la explicación relacionada con el "bit de signo" se da en esa publicación, no puedo entender cómo es posible que configuremos algo y luego no se refleja como está.
¿Alguien puede dar una explicación más elaborada?
Nota : Se requieren ambas etiquetas c & c ++ , porque sus estándares difieren ligeramente para describir los campos de bits. Vea las respuestas para la especificación C y la especificación C ++ .
Este es un comportamiento de implementación definido.
Supongo que las máquinas en las que está ejecutando esto utilizan dos enteros con signo de complemento y, en este caso, tratan el
int
como un entero con signo para explicar por qué no ingresa if parte verdadera de la declaración if.
struct mystruct { int enabled:1; };
declara
enable
como un campo de bits de 1 bit.
Dado que está firmado, los valores válidos son
-1
y
0
.
Configurando el campo en
1
desborda ese bit regresando a
-1
(esto es un comportamiento indefinido)
Esencialmente, cuando se trata de un campo de bits firmado, el valor máximo es
2^(bits - 1) - 1
que es
0
en este caso.
Los campos de bits están increíblemente mal definidos por el estándar.
Dado este código
struct mystruct {int enabled:1;};
, entonces
no
sabemos
- Cuánto espacio ocupa esto - si hay bits / bytes de relleno y dónde están ubicados en la memoria.
- Donde el bit se encuentra en la memoria. No definido y también depende de la endianess.
-
Si un campo de bits
int:n
debe considerarse como firmado o sin firmar.
Respecto a la última parte, C17 6.7.2.1/10 dice:
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)
Nota no normativa que explica lo anterior:
125) Como se especifica en 6.7.2 arriba, si el especificador de tipo real utilizado es
int
o un typedef-name definido comoint
, entonces está definido por la implementación si el campo de bits está firmado o sin firmar.
En caso de que el campo de bits se considere como
signed int
y usted haga un poco del tamaño
1
, entonces no hay espacio para los datos, solo para el bit de signo.
Esta es la razón por la que su programa puede dar resultados extraños en algunos compiladores.
Buena práctica:
- Nunca utilice campos de bits para ningún propósito.
-
Evite utilizar el tipo
int
firmado para cualquier forma de manipulación de bits.
No hay nada de malo en tu comprensión de los campos de bits que puedo ver. Lo que veo es que primero redefiniste mystruct como struct mystruct {int enabled: 1; } y luego como estructurar mystruct s; . Lo que deberías haber codificado fue:
#include <stdio.h>
struct mystruct { int enabled:1; };
int main()
{
mystruct s; <-- Get rid of "struct" type declaration
s.enabled = 1;
if(s.enabled == 1)
printf("Is enabled/n"); // --> we think this to be printed
else
printf("Is disabled !!/n");
}
Se podría pensar que en el sistema de complemento a 2, el bit de más a la izquierda es el bit de signo. Cualquier entero con signo con el conjunto de bits más a la izquierda es, por lo tanto, un valor negativo.
Si tiene un entero con signo de 1 bit, solo tiene el bit de signo.
Por lo tanto, asignar
1
a ese único bit solo puede establecer el bit de signo.
Entonces, al leerlo de nuevo, el valor se interpreta como negativo y también es -1.
Los valores que puede contener un entero con signo de 1 bit es
-2^(n-1)= -2^(1-1)= -2^0= -1
y
2^n-1= 2^1-1=0
Según el
estándar C ++ n4713
, se proporciona un fragmento de código muy similar.
El tipo utilizado es
BOOL
(personalizado), pero puede aplicarse a cualquier tipo.
12.2.4
4 Si el valor verdadero o falso se almacena en un campo de bits de tipo
bool
de cualquier tamaño (incluido un campo de bits de un bit), el valor original debool
y el valor del campo de bits se compararán igual. Si el valor de un enumerador se almacena en un campo de bits del mismo tipo de enumeración y el número de bits en el campo de bits es lo suficientemente grande como para contener todos los valores de ese tipo de enumeración (10.2), el valor original del enumerador y valor del campo de bits se comparará igual . [Ejemplo:
enum BOOL { FALSE=0, TRUE=1 }; struct A { BOOL b:1; }; A a; void f() { a.b = TRUE; if (a.b == TRUE) // yields true { /* ... */ } }
- ejemplo final]
A primera vista, la parte en negrita aparece abierta para la interpretación.
Sin embargo, la intención correcta queda clara cuando la
enum BOOL
se deriva de la
int
.
enum BOOL : int { FALSE=0, TRUE=1 }; // ***this line
struct mystruct { BOOL enabled:1; };
int main()
{
struct mystruct s;
s.enabled = TRUE;
if(s.enabled == TRUE)
printf("Is enabled/n"); // --> we think this to be printed
else
printf("Is disabled !!/n");
}
Con el código anterior da una advertencia sin
-Wall -pedantic
:
advertencia: ''mystruct :: enabled'' es demasiado pequeño para contener todos los valores de ''enum BOOL''
struct mystruct { BOOL enabled:1; };
struct mystruct { BOOL enabled:1; };
La salida es:
Está desactivado !! (cuando se utiliza
enum BOOL : int
)
Si
enum BOOL : int
se hace simple
enum BOOL
, entonces la salida es como especifica el pasaje estándar anterior:
Está habilitado (cuando se utiliza
enum BOOL
)
Por lo tanto, se puede concluir, también como pocas otras respuestas tienen, que el
tipo
int
no es lo suficientemente grande como para almacenar el valor "1" en un solo bit de campo de bits.