He visto que este patrón se usa mucho en C y 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?
c++
c
binary
bit-fields
hiperlogico
fuente
fuente
-1
siempre funcionará, el hecho de que se necesite un comentario después de que demuestre que no es un código claro. Si la variable está destinada a ser una colección de banderas, ¿por qué asignarle un número entero? Su tipo puede ser un entero, pero ciertamente no es semánticamente un entero. Nunca lo vas a incrementar o multiplicar. Por lo tanto, no lo usaría0xffffffff
para portabilidad o corrección, sino para mayor claridad.-1
sigue siendo una solución portátil y compatible con versiones anteriores para ambos idiomas, pero podría afectar parte del razonamiento en otras respuestas.Respuestas:
Le recomiendo que lo haga exactamente como lo ha mostrado, ya que es el más sencillo. Inicialice a
-1
lo que funcionará siempre , independientemente de la representación de signo real, mientras~
que a veces tendrá un comportamiento sorprendente porque tendrá que tener el tipo de operando correcto. Solo así obtendrá el valor más alto de ununsigned
tipo.Para un ejemplo de una posible sorpresa, considere este:
No almacenará necesariamente un patrón con todos los bits 1 en
a
. Pero primero creará un patrón con todos los bits 1 en ununsigned int
, y luego lo asignará aa
. Lo que sucede cuandounsigned long
tiene más bits es que no todos son 1.Y considere este, que fallará en una representación de complemento de no dos:
La razón de esto es que
~0
tiene que invertir todos los bits. Invirtiendo que producirá-1
en la máquina de complemento a dos (que es el valor que necesitamos!), Sino que no se dió-1
en otra representación. En la máquina de complemento de uno, produce cero. Por lo tanto, en la máquina de complemento de uno, lo anterior se inicializaráa
a cero.Lo que debe 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
oUINT_MAX
. El segundo dependerá del tipo dea
- necesitará usarULONG_MAX
para ununsigned long
. Sin embargo, el primero no dependerá de su tipo, y es una buena manera de obtener el mayor valor.Estamos no hablando de si
-1
tiene todos los bits de uno (que no siempre tiene). Y estamos no hablar acerca de si~0
tiene todos los bits (uno que tiene, por supuesto).Pero de lo que estamos hablando es de cuál es el resultado de la
flags
variable inicializada . Y para ello, solo-1
funcionará con todo tipo y máquina.fuente
numeric_limits<size_t>::max()
es un poco largo aliento, pero también lo es el elenco ...-1
está representado, ni pregunta qué bits~0
tiene. Puede que no nos interesen los valores, pero al compilador sí. No podemos ignorar el hecho de que las operaciones funcionan con y por valores. El valor de~0
puede no ser-1
, pero este es el valor que necesita. Vea mi respuesta y el resumen de @ Dingo.unsigned int flags = -1;
Es portátil.unsigned int flags = ~0;
no es portátil porque se basa en una representación de complemento a dos.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.
fuente
~0
produce unint
valor con todos los bits establecidos, por supuesto. Pero asignar unint
anunsigned int
no necesariamente da como resultado que el int sin signo tenga el mismo patrón de bits que el patrón de bits con signo. Solo con una representación de complemento a 2 es siempre este el caso. En un complemento de 1s o representación de magnitud de signo, la asignación de unint
valor negativo a ununsigned int
resultado en un patrón de bits diferente. Esto se debe a que el estándar C ++ define la conversión con signo -> sin signo para que sea el valor de módulo igual, no el valor con los mismos bits.Francamente, creo que todos los fff son más legibles. En cuanto al comentario de que es un antipatrón, si realmente le importa que todos los bits estén configurados / borrados, diría que probablemente se encuentre en una situación en la que le importa el tamaño de la variable de todos modos, lo que requeriría algo como impulso :: uint16_t, etc.
fuente
Una forma de evitar los problemas mencionados es simplemente hacer:
Portátil y al punto.
fuente
flags
comoconst
.unsigned int const flags = ~0u;
~0
es un número entero que tiene todos los bits establecidos en 1, pero cuando lo asignaint
a launsigned
variableflags
, realiza una conversión de valor de-2**31
(suponiendo un 32 bitsint
) a(-2**31 % 2**32) == 2**31
, que es un número entero con todos los bits excepto el primer set en 1.u
sufijo en tu respuesta. Eso, por supuesto, funcionaría, pero aún tiene el problema de especificar el tipo de datos que usa (unsigned
y no más grande) dos veces, lo que podría provocar errores. Sin embargo, es más probable que aparezca el error si la asignación y la declaración de variable inicial están más separadas.No estoy seguro de usar un unsigned int para banderas es una buena idea en primer lugar en C ++. ¿Qué pasa con bitset y similares?
std::numeric_limit<unsigned int>::max()
es mejor porque0xffffffff
supone que unsigned int es un entero de 32 bits.fuente
auto
.auto const flags = std::numeric_limit<unsigned>::max()
.¿Portátil? Sí .
¿Bueno? Debatible , como lo demuestra toda la confusión que se muestra en este hilo. Ser lo suficientemente claro para que sus compañeros 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 su compilador, necesitaría un reparto explícito. Por ejemplo,
El lanzamiento explícito requiere que prestes atención al tipo de destino. Si presta atención al tipo de objetivo, entonces, naturalmente, evitará las trampas de los otros enfoques.
Mi consejo sería prestar atención al tipo de destino y asegurarse de que no haya conversiones implícitas. Por ejemplo:
Todo lo cual es correcto y más obvio para sus compañeros programadores.
Y con C ++ 11 : podemos usar
auto
para simplificar cualquiera de estos:Considero correcto y obvio mejor que simplemente correcto.
fuente
La conversión de -1 en cualquier tipo sin signo está garantizada por el estándar para dar como resultado todos. El uso de
~0U
es generalmente malo ya que0
tiene tipounsigned int
y no llenará todos los bits de un tipo sin signo más grande, a menos que escriba explícitamente algo como~0ULL
. En sistemas sanos,~0
debería ser idéntico a-1
, pero dado que el estándar permite representaciones de complemento y signo / magnitud, estrictamente hablando no es portátil.Por supuesto, siempre está bien escribir
0xffffffff
si sabe que necesita exactamente 32 bits, pero -1 tiene la ventaja de que funcionará en cualquier contexto, incluso si no conoce el tamaño del tipo, como las macros que funcionan en varios tipos , o si el tamaño del tipo varía según la implementación. Si lo hace saber el tipo, otra forma segura de obtener todos unos macros es el límiteUINT_MAX
,ULONG_MAX
,ULLONG_MAX
, etc.Personalmente siempre uso -1. Siempre funciona y no tienes que pensarlo.
fuente
~(type)0
(bueno, complete el derecho,type
por supuesto). Lanzar cero todavía da como resultado un cero, por lo que está claro, y negar todos los bits en el tipo de destino está claramente definido. Sin embargo, no es tan frecuente que realmente quiera esa operación; YMMV.neg
instrucción. Las máquinas que tienen un comportamiento aritmético con signo falso tienen códigos opcionales aritméticos con signo / sin signo. Por supuesto, un compilador realmente bueno ignoraría siempre los códigos de operación firmados incluso para los valores firmados y, por lo tanto, obtendría dos complementos de forma gratuita.var = ~(0*var)
caso fallará porvar
ser un tipo sin signo más estrecho queint
. Tal vezvar = ~(0U*var)
? (Personalmente, todavía prefiero-1
, sin embargo).Siempre que tenga
#include <limits.h>
uno de sus incluidos, solo debe usarSi quieres un poco de bits, puedes usar
Se garantiza que estos valores tienen todos los bits de valor del resultado establecidos en 1, independientemente de cómo se implementen los enteros con signo.
fuente
Si. Como se menciona en otras respuestas,
-1
es el más portátil; sin embargo, no es muy semántico y activa advertencias del compilador.Para resolver estos problemas, pruebe este simple ayudante:
Uso:
fuente
ALL_BITS_TRUE ^ a
dondea
está un entero con signo? El tipo permanece entero con signo y el patrón de bits (representación de objeto) depende de que el objetivo sea el complemento de 2 o no.ALL_BITS_TRUE ^ a
da un error de compilación porqueALL_BITS_TRUE
es ambiguo. Podría ser utilizado comouint32_t(ALL_BITS_TRUE) ^ a
, sin embargo. Puede probarlo usted mismo en cpp.sh :) Hoy en día agregaría unstatic_assert(std::is_unsigned<UnsignedType>::value, "This is designed only for unsigned types");
en eloperator
para asegurarse de que los usuarios no intenten usarloint(ALL_BITS_TRUE)
. Actualizaré la respuesta.No haría la cosa -1. Es bastante no intuitivo (al menos para mí). Asignar datos firmados a una variable sin firmar simplemente parece ser una violación del orden natural de las cosas.
En tu situación, siempre uso
0xFFFF
. (Utilice el número correcto de Fs para el tamaño variable, por supuesto).[Por cierto, rara vez veo el truco -1 hecho en el código del mundo real.]
Además, si realmente se preocupan por los bits individuales en una vairable, sería una buena idea para empezar a utilizar el ancho fijo
uint8_t
,uint16_t
,uint32_t
tipos.fuente
En los procesadores Intel IA-32 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 a IA32) solo admite elementos inmediatos de 32 bits. En las instrucciones de 64 bits, los inmediatos de 32 bits se extienden por signos a 64 bits.
Lo siguiente es ilegal:
Lo siguiente pone 64 1s en RAX:
Solo para completar, lo siguiente coloca 32 1s en la parte inferior de RAX (también conocido como EAX):
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:
el resultado es:
Pensé en publicar esto como un comentario a todas las respuestas que decían que 0xFFFFFFFF asume 32 bits, pero tanta gente lo respondió que pensé que lo agregaría como una respuesta separada.
fuente
UINT64_C(0xffffffff)
expande a algo así0xffffffffuLL
, definitivamente es un error del compilador. El estándar C analiza en gran medida los valores , el valor representado por0xffffffff
es 4294967295 (no 36893488147419103231), y no hay conversiones a tipos enteros con signo a la vista.Vea la respuesta de litb para una explicación muy clara de los problemas.
Mi desacuerdo es que, estrictamente hablando, no hay garantías para ninguno de los casos. No conozco ninguna arquitectura que no represente un valor sin signo de 'uno menos que dos a la potencia del número de bits' como todos los bits configurados, pero esto es lo que el Estándar realmente dice (3.9.1 / 7 más nota 44):
Eso deja la posibilidad de que uno de los bits sea cualquier cosa.
fuente
Aunque el
0xFFFF
(o0xFFFFFFFF
, etc.) puede ser más fácil de leer, puede romper la portabilidad del 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 especificados por la persona que llama). La rutina puede ser totalmente agnóstica en cuanto a lo que representan los bits, pero aún debe tener una constante de "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
typedef
se usa un valor para la máscara de bits, sería usar ~ (bitMaskType) 0; Si la máscara de bits es solo un tipo de 16 bits, esa expresión solo tendrá 16 bits establecidos (incluso si 'int' sería de otro modo 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 typecast.Por cierto, las expresiones de la forma
longvar &= ~[hex_constant]
tienen un problema desagradable si la constante hexadecimal es demasiado grande para caber en unint
, pero encajará en ununsigned int
. Si anint
es de 16 bits, entonceslongvar &= ~0x4000;
olongvar &= ~0x10000
; borrará un bit delongvar
, perolongvar &= ~0x8000;
borrará el bit 15 y todos los bits por encima de eso. Los valores que encajanint
tendrán el operador del complemento aplicado a un tipoint
, pero el resultado se extenderá along
, configurando los bits superiores. Los valores que son demasiado grandesunsigned int
tendrán el operador del complemento aplicado al tipolong
. Sin embargo, los valores que se encuentran entre esos tamaños aplicarán el operador de complemento al tipounsigned int
, que luego se convertirá a tipolong
sin extensión de signo.fuente
Prácticamente: si
Teóricamente: no.
-1 = 0xFFFFFFFF (o cualquier tamaño que tenga un int en su plataforma) solo es cierto con la aritmética del complemento a dos. En la práctica, funcionará, pero hay máquinas heredadas (mainframes de IBM, etc.) donde tienes un bit de signo real en lugar de una representación de complemento a dos. Su solución ~ 0 propuesta debería funcionar en todas partes.
fuente
Como otros han mencionado, -1 es la forma correcta de crear un número entero que se convertirá en un tipo sin signo con todos los bits establecidos en 1. Sin embargo, lo más importante en C ++ es usar los tipos correctos. Por lo tanto, la respuesta correcta a su problema (que incluye la respuesta a la pregunta que hizo) es esta:
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.fuente
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.fuente
Yo digo:
Esto siempre te dará el resultado deseado.
fuente
Aprovechar el hecho de que asignar todos los bits a uno para un tipo sin signo es equivalente a tomar el valor máximo posible para el tipo dado
y extender el alcance de la pregunta a todos los tipos 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:
<limits>
y usarstd::numeric_limits< your_type >::max()
El propósito podría ser agregar más claridad, ya que la asignación
-1
siempre necesitaría algún comentario explicativo.fuente
Una forma de hacer que el significado sea un poco más obvio y, sin embargo, evitar repetir el tipo:
fuente
Sí, la representación mostrada es muy correcta, ya que si lo hacemos al revés, requerirá que un operador invierta todos los bits, pero en este caso la lógica es bastante sencilla si consideramos el tamaño de los enteros en la máquina
Por ejemplo, en la mayoría de las máquinas, un número entero es de 2 bytes = el valor máximo de 16 bits 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 sencillo
supongo
no, si considera esta noción, es perfectamente ideal para entradas sin signo y realmente funciona
solo verifique el siguiente fragmento de programa
int main () {
}
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, por favor informe
fuente