Pregunta

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?

¿Fue útil?

Solución

Te recomiendo que lo hagas exactamente como has mostrado, ya que es el más sencillo. Inicialice a -1 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 entonces obtendrá el valor más alto de un tipo unsigned.

Para ver 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 unsigned long. Lo que sucede cuando ~0 tiene más bits es que no todos son 1.

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

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

La razón de esto es que UINT_MAX tiene que invertir todos los bits. Invertir eso producirá ULONG_MAX en una máquina complementaria de dos (¡que es el valor que necesitamos!), Pero no rendirá flags 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 inicializará <=> 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 <=> al valor más alto posible es <=> o <=>. El segundo dependerá del tipo de <=>: deberá usar <=> para un <=>. Sin embargo, el primero no dependerá de su tipo, y es una buena manera de obtener el mayor valor.

no estamos hablando de si <=> tiene todos los bits uno (no siempre los tiene). Y no estamos hablando de si <=> tiene todos los bits uno (tiene, por supuesto).

Pero de lo que estamos hablando es de cuál es el resultado de la variable <=> inicializada. Y para ello, solo <=> funcionará con cada tipo y máquina.

Otros consejos

  • 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 asume entradas de 32 bits.

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

Francamente, creo que todas las 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.

Una forma de evitar los problemas mencionados es simplemente hacer:

unsigned int flags = 0;
flags = ~flags;

Portátil y al grano.

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 porque 0xffffffff supone que unsigned int es un entero de 32 bits.

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

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

Portátil? .

¿Bien? Debatable , como lo demuestra toda la confusión que se muestra en este hilo. Ser lo suficientemente claro como 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 advertencias del compilador . Para eludir la advertencia sin paralizar su compilador, necesitaría un reparto explícito. Por ejemplo,

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

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:

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 sus compañeros programadores.

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

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.

Convertir -1 a cualquier tipo sin firmar es garantizado por la norma para dar como resultado todos unos.Uso de ~0U es generalmente malo ya que 0 tiene tipo unsigned int y no llenará todos los bits de un tipo sin firmar más grande, a menos que escriba explícitamente algo como ~0ULL.En sistemas sanos, ~0 debe ser identico a -1, pero dado que el estándar permite representaciones de complemento a uno y de signo/magnitud, estrictamente hablando no es portátil.

Por supuesto que siempre está bien escribir 0xffffffff si sabe que necesita exactamente 32 bits, pero -1 tiene la ventaja de que funcionará en cualquier contexto incluso cuando no conozca el tamaño del tipo, como 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 unos son las macros de límite. UINT_MAX, ULONG_MAX, ULLONG_MAX, etc.

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

Sí 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 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 tenga #include <limits.h> como una de sus inclusiones, solo debe usar

unsigned int flags = UINT_MAX;

Si quieres un poco de bits, puedes usar

unsigned long flags = ULONG_MAX;

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

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 su 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 le importan los bits individuales en un valor deseable, sería una buena idea comenzar a usar los tipos uint8_t, uint16_t, uint32_t de ancho fijo.

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 son signo-extendido a 64 bits.

Lo siguiente es ilegal:

mov rax, 0ffffffffffffffffh

Lo siguiente pone 64 1s en RAX:

mov rax, 0ffffffffh

Solo para completar, lo siguiente coloca 32 1s en la parte inferior de RAX (también conocido como EAX):

mov eax, 0ffffffffh

Y, de hecho, he tenido fallas 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 a todas las respuestas que decían que 0xFFFFFFFF supone 32 bits, pero tanta gente respondió que pensé que lo agregaría como una respuesta separada.

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

Mi desacuerdo es que, muy 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):

  

Las representaciones de tipos integrales definirán valores mediante el uso de un sistema de numeración binaria 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 quizás por el bit con la posición más alta.

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

Prácticamente: Sí

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 propuesta de solución ~ 0 debería funcionar en todas partes.

Aunque el 0xFFFF (o 0xFFFFFFFF, etc.) puede ser más fácil de leer, puede romper la portabilidad en el 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 un & "; Todos los bits establecidos &"; constante. 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 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 como 16 bits serán todo lo que se requiere, las cosas deberían estar bien proporcionado que uno realmente usa el tipo apropiado en la conversión de texto.

Por cierto, las expresiones de la forma longvar &= ~[hex_constant] tienen una mala idea si la constante hexadecimal es demasiado grande para caber en un int, pero cabe en un unsigned int. Si un longvar &= ~0x4000; es de 16 bits, entonces longvar &= ~0x10000 o longvar; borrará un bit de longvar &= ~0x8000;, pero long borrará el bit 15 y todos los bits por encima de eso. Los valores que se ajustan en <=> tendrán el operador de complemento aplicado a un tipo <=>, pero el resultado se extenderá a <=>, configurando los bits superiores. Los valores que son demasiado grandes para <=> tendrán el operador de complemento aplicado al tipo <=>. Sin embargo, los valores que están entre esos tamaños aplicarán el operador del complemento para escribir <=>, que luego se convertirá al tipo <=> sin extensión de signo.

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:

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.

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.

Yo digo:

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

Esto siempre te dará el resultado deseado.

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

Asignar -1 funciona para cualquier no firmado tipo entero (unsigned int, uint8_t, uint16_t, etc.) tanto para C como para C++.

Como alternativa, para C++, puedes:

  1. Incluir <limits> y use std::numeric_limits< your_type >::max()
  2. Escribe un función de plantilla personalizada (Esto también permitiría realizar algunas comprobaciones de cordura, es decir,si el tipo de destino es realmente un tipo sin firmar)

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

Una forma de hacer que el significado sea un poco más obvio y, sin embargo, evitar repetir el tipo:

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

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 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 más 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 () {

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 plzz

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top