fields c++ c binary bit-fields

c++ - fields - ¿Es seguro usar-1 para establecer todos los bits en verdadero?



bit field c++ (19)

unsigned int flags = -1; // all bits are true

"¿Es esta una buena [,] forma portátil de lograr esto?"

¿Portátil? .

¿Bueno? Debatable , como lo demuestra toda la confusión que se muestra en este hilo. Tener la claridad suficiente para que los demás programadores puedan entender el código sin confusión debería ser una de las dimensiones que medimos para un buen código.

Además, este método es propenso a las advertencias del compilador . Para eludir la advertencia sin paralizar tu compilador, necesitarías un lanzamiento explícito. Por ejemplo,

unsigned int flags = static_cast<unsigned int>(-1);

El lanzamiento explícito requiere que prestes atención al tipo de objetivo. Si prestas atención al tipo de objetivo, naturalmente evitarás las trampas de los otros enfoques.

Mi consejo sería prestar atención al tipo de objetivo y asegurarse de que no haya conversiones implícitas. Por ejemplo:

unsigned int flags1 = UINT_MAX; unsigned int flags2 = ~static_cast<unsigned int>(0); unsigned long flags3 = ULONG_MAX; unsigned long flags4 = ~static_cast<unsigned long>(0);

Todos los cuales son correctos y más obvios para tus compañeros programadores.

Y con C ++ 11 : podemos usar auto para hacer cualquiera de estos aún más simple:

auto flags1 = UINT_MAX; auto flags2 = ~static_cast<unsigned int>(0); auto flags3 = ULONG_MAX; auto flags4 = ~static_cast<unsigned long>(0);

Considero correcto y obvio mejor que simplemente correcto.

He visto este patrón usado mucho en C & C ++.

unsigned int flags = -1; // all bits are true

¿Es esta una buena forma portátil de lograr esto? ¿O está usando 0xffffffff o ~0 mejor?


Aprovechando el hecho de que asignar todos los bits a uno para un tipo sin signo es equivalente a tomar el máximo valor posible para el tipo dado,
y ampliar el alcance de la pregunta a todos los tipos de enteros sin signo:

Asignar -1 funciona para cualquier tipo de entero sin signo (unsigned int, uint8_t, uint16_t, etc.) para C y C ++.

Como alternativa, para C ++, puede:

  1. Incluye <limits> y usa std::numeric_limits< your_type >::max()
  2. Escribir una función de plantilla personalizada (Esto también permitiría una cierta verificación de cordura, es decir, si el tipo de destino es realmente un tipo sin signo)

El propósito podría ser agregar más claridad, ya que asignar -1 siempre necesitaría algún comentario explicativo.


Aunque el 0xFFFF (o 0xFFFFFFFF , etc.) puede ser más fácil de leer, puede romper la portabilidad en un código que de otro modo sería portátil. Considere, por ejemplo, una rutina de biblioteca para contar cuántos elementos en una estructura de datos tienen determinados bits establecidos (los bits exactos son especificados por la persona que llama). La rutina puede ser totalmente agnóstica en cuanto a lo que representan los bits, pero aún necesita tener una constante "todos los bits establecidos". En tal caso, -1 será mucho mejor que una constante hexadecimal, ya que funcionará con cualquier tamaño de bit.

La otra posibilidad, si se usa un valor typedef para la máscara de bits, sería usar ~ (bitMaskType) 0; si la máscara de bits pasa a ser solo un tipo de 16 bits, esa expresión solo tendrá 16 bits configurados (incluso si ''int'' fuera de 32 bits) pero dado que 16 bits serán todo lo que se requiere, las cosas deberían estar bien siempre que en realidad usa el tipo apropiado en el tipocast.

A propósito, las expresiones de la forma longvar &= ~[hex_constant] tienen un desagradable error si la constante hexadecimal es demasiado grande para caber en un int , pero encajará en un unsigned int . Si un int es de 16 bits, entonces longvar &= ~0x4000; o longvar &= ~0x10000 ; borrará un poco de longvar , pero longvar &= ~0x8000; borrará el bit 15 y todos los bits por encima de eso. Los valores que se ajustan a int tendrán el operador de complemento aplicado a un tipo int , pero el resultado será signo extendido a long , estableciendo los bits superiores. Los valores que son demasiado grandes para unsigned int tendrán el operador de complemento aplicado para escribir long . Sin embargo, los valores que están entre esos tamaños aplicarán el operador del complemento para escribir unsigned int , que luego se convertirá en tipo long sin extensión de signo.


Ciertamente es seguro, ya que -1 siempre tendrá todos los bits disponibles, pero me gusta ~ 0 mejor. -1 simplemente no tiene mucho sentido para un unsigned int . 0xFF ... no es bueno porque depende del ancho del tipo.


Como han mencionado otros, -1 es la forma correcta de crear un entero que se convertirá en un tipo sin firmar con todos los bits establecidos en 1. Sin embargo, lo más importante en C ++ es usar tipos correctos. Por lo tanto, la respuesta correcta a su problema (que incluye la respuesta a la pregunta que hizo) es esta:

std::bitset<32> const flags(-1);

Esto siempre contendrá la cantidad exacta de bits que necesita. Construye un std::bitset con todos los bits establecidos en 1 por las mismas razones mencionadas en otras respuestas.


En los procesadores IA-32 de Intel, está bien escribir 0xFFFFFFFF en un registro de 64 bits y obtener los resultados esperados. Esto se debe a que IA32e (la extensión de 64 bits para IA32) solo admite eventos inmediatos de 32 bits. En las instrucciones de 64 bits, las inmediatas de 32 bits se extienden a 64 bits.

Lo siguiente es ilegal:

mov rax, 0ffffffffffffffffh

Lo siguiente pone 64 1s en RAX:

mov rax, 0ffffffffh

Para completar, lo siguiente pone 32 1 en la parte inferior de RAX (también conocido como EAX):

mov eax, 0ffffffffh

Y, de hecho, he tenido errores en los programas cuando quería escribir 0xffffffff en una variable de 64 bits y obtuve un 0xffffffffffffffff en su lugar. En C esto sería:

uint64_t x; x = UINT64_C(0xffffffff) printf("x is %"PRIx64"/n", x);

el resultado es:

x is 0xffffffffffffffff

Pensé en publicar esto como un comentario para todas las respuestas que decían que 0xFFFFFFFF supone 32 bits, pero mucha gente respondió que pensé en agregarlo como una respuesta separada.


Francamente, creo que todos los FFF son más legibles. En cuanto al comentario de que es un antipatrón, si realmente te importa que todos los bits estén configurados / eliminados, yo diría que probablemente estés en una situación en la que te importe el tamaño de la variable de todos modos, lo que requeriría algo como impulsar :: uint16_t, etc.


La conversión de -1 a cualquier tipo sin signo está garantizada por el estándar para dar como resultado todos. El uso de ~0U es generalmente malo ya que 0 tiene el tipo unsigned int y no llena todos los bits de un tipo sin signo más grande, a menos que explícitamente escriba algo como ~0ULL . En los sistemas sanos, ~0 debe ser idéntico a -1 , pero como el estándar permite representaciones de complemento y signo / magnitud, estrictamente hablando no es portátil.

Por supuesto, siempre está bien escribir 0xffffffff si sabes que necesitas exactamente 32 bits, pero -1 tiene la ventaja de que funcionará en cualquier contexto incluso cuando no sepas el tamaño del tipo, como las macros que funcionan en múltiples tipos, o si el tamaño del tipo varía según la implementación. Si conoce el tipo, otra forma segura de obtener todos es el límite de macros UINT_MAX , ULONG_MAX , ULLONG_MAX , etc.

Personalmente siempre uso -1. Siempre funciona y no tienes que pensar en eso.


No estoy seguro de usar una int sin signo para las banderas es una buena idea en primer lugar en C ++. ¿Qué hay de bitset y similares?

std::numeric_limit<unsigned int>::max() es mejor porque 0xffffffff supone que unsigned int es un entero de 32 bits.


Prácticamente: sí

Teóricamente: No.

-1 = 0xFFFFFFFF (o el tamaño que tenga int en su plataforma) solo es cierto con la aritmética de dos complementos. En la práctica, funcionará, pero existen máquinas heredadas (mainframes de IBM, etc.) donde tiene un bit de signo real en lugar de una representación de complemento de dos. Su propuesta ~ 0 solución debería funcionar en todas partes.


Sí. Como se menciona en otras respuestas, -1 es el más portátil; sin embargo, no es muy semántico y desencadena advertencias del compilador.

Para resolver estos problemas, intente con este sencillo ayudante:

static const struct All1s { template<typename UnsignedType> inline operator UnsignedType(void) const { return static_cast<UnsignedType>(-1); } } ALL_BITS_TRUE;

Uso:

unsigned a = ALL_BITS_TRUE; uint8_t b = ALL_BITS_TRUE; uint16_t c = ALL_BITS_TRUE; uint32_t d = ALL_BITS_TRUE; uint64_t e = ALL_BITS_TRUE;


Siempre que tengas #include <limits.h> como uno de tus includes, solo deberías usar

unsigned int flags = UINT_MAX;

Si quieres una larga cantidad de bits, podrías usar

unsigned long flags = ULONG_MAX;

Se garantiza que estos valores tienen todos los bits de valor del conjunto de resultados en 1, independientemente de cómo se implementen los enteros con signo.


Te recomiendo que lo hagas exactamente como lo has mostrado, ya que es el más directo. Inicialice en -1 que funcionará siempre , independientemente de la representación del signo real, mientras que ~ a veces tendrá un comportamiento sorprendente, ya que tendrá que tener el tipo de operando correcto. Solo así obtendrás el valor más alto de un tipo unsigned .

Para un ejemplo de una posible sorpresa, considere esta:

unsigned long a = ~0u;

No necesariamente almacenará un patrón con todos los bits 1 en a . Pero primero creará un patrón con todos los bits 1 en un unsigned int , y luego lo asignará a. Lo que sucede cuando unsigned long tiene más bits es que no todos ellos son 1.

Y considere este, que fallará en una representación complementaria de no dos:

unsigned int a = ~0; // Should have done ~0u !

La razón para eso es que ~0 tiene que invertir todos los bits. Invertir eso producirá -1 en una máquina complementaria de dos (¡que es el valor que necesitamos!), Pero no rendirá -1 en otra representación. En una máquina complementaria, produce cero. Por lo tanto, en una máquina complementaria de uno, lo anterior inicializará a a cero.

Lo que debes entender es que se trata de valores, no de bits. La variable se inicializa con un valor . Si en el inicializador modifica los bits de la variable utilizada para la inicialización, el valor se generará de acuerdo con esos bits. El valor que necesita para inicializar a al valor más alto posible es -1 o UINT_MAX . El segundo dependerá del tipo de a : necesitará usar ULONG_MAX para un unsigned long . Sin embargo, el primero no dependerá de su tipo, y es una buena manera de obtener el mayor valor.

No estamos hablando de si -1 tiene todos los bits uno (no siempre tiene). Y no estamos hablando de si ~0 tiene todos los bits uno (tiene, por supuesto).

Pero de lo que estamos hablando es de cuál es el resultado de la variable de flags inicializados. Y para ello, solo -1 funcionará con todos los tipos y máquinas.


Una forma de evitar los problemas mencionados es simplemente hacer:

unsigned int flags = 0; flags = ~flags;

Portátil y al grano.


Vea la respuesta de litb para una explicación muy clara de los problemas.

Mi desacuerdo es que, en sentido estricto, no hay garantías para ninguno de los casos. No conozco ninguna arquitectura que no represente un valor sin signo de ''uno menos de dos para la potencia de la cantidad de bits'' como todos los bits establecidos, pero esto es lo que realmente dice el Estándar (3.9.1 / 7 más nota 44):

Las representaciones de tipos integrales definirán valores mediante el uso de un sistema de numeración binario puro. [Nota 44:] Una representación posicional para enteros que usa los dígitos binarios 0 y 1, en los que los valores representados por bits sucesivos son aditivos, comienzan con 1 y se multiplican por la potencia integral sucesiva de 2, excepto tal vez para el bit con la posición más alta.

Eso deja la posibilidad de que uno de los bits sea cualquier cosa.


Yo digo:

int x; memset(&x, 0xFF, sizeof(int));

Esto siempre te dará el resultado deseado.


Yo no haría lo -1. No es bastante intuitivo (al menos para mí). Asignar datos firmados a una variable sin signo solo parece ser una violación del orden natural de las cosas.

En su situación, siempre uso 0xFFFF . (Use el número correcto de F para el tamaño variable del curso).

[Por cierto, muy rara vez veo el truco -1 en el código del mundo real.]

Además, si realmente te interesan los bits individuales en un vairable, sería una buena idea empezar a usar los tipos uint8_t , uint16_t , uint32_t ancho uint8_t .


sí, la representación mostrada es muy correcta, como si lo hiciéramos al revés, requerirá que un operador revierte todos los bits, pero en este caso la lógica es bastante directa si consideramos el tamaño de los enteros en la máquina

por ejemplo, en la mayoría de las máquinas, un entero tiene 2 bytes = 16 bits de valor máximo que puede contener es 2 ^ 16-1 = 65535 2 ^ 16 = 65536

0% 65536 = 0 -1% 65536 = 65535 que corresponde a 1111 ............. 1 y todos los bits se establecen en 1 (si consideramos las clases de residuos mod 65536) por lo tanto, es mucho directo hacia adelante

supongo

no, si consideras esta noción, es perfectamente recomendable para ints sin firmar y realmente funciona

solo revisa el siguiente fragmento de programa

int main () {

unsigned int a=2; cout<<(unsigned int)pow(double(a),double(sizeof(a)*8)); unsigned int b=-1; cout<<"/n"<<b; getchar(); return 0;

}

respuesta para b = 4294967295 que es -1% 2 ^ 32 en enteros de 4 bytes

por lo tanto, es perfectamente válido para enteros sin signo

en caso de discrepancias, informe de plzz


  • unsigned int flags = -1; es portátil.
  • unsigned int flags = ~0; no es portátil porque se basa en una representación de dos complementos.
  • unsigned int flags = 0xffffffff; no es portátil porque supone entradas de 32 bits.

Si desea establecer todos los bits de una manera garantizada por el estándar C, use el primero.