2009-10-27 23 views
11

no estoy muy acostumbrado a la programación con banderas, pero creo que acabo de encontrar una situación en la que serían de utilidad:Banderas, enumeración (C)

Tengo un par de objetos que registran a sí mismos como oyentes de ciertos eventos. Para qué eventos se registran depende de una variable que se les envía cuando se construyen. Creo que una buena manera de hacer esto sería enviar variables bit a bit O conectadas, como estas: TAKES_DAMAGE | GRABBABLE | LIQUID, etc. Luego, en el constructor, el objeto puede verificar qué banderas se configuran y registrarlo como oyente para las que sí lo están.

Pero aquí es donde me confundo. Preferiblemente, las banderas estarían en una enumeración. Pero eso también es un problema. Si tenemos estas banderas:

enum 
{ 
    TAKES_DAMAGE,/* (0) */ 
    GRABBABLE, /* (1) */ 
    LIQUID, /* (2) */ 
    SOME_OTHER /* (3) */ 
}; 

a continuación, enviar el SOME_OTHER bandera (3) será el mismo que enviar GRABBABLE | LIQUIDO, ¿no es así?

¿Cómo se maneja exactamente esto?

Respuesta

45

Su enumeración debe ser potencias de dos:

enum 
{ 
    TAKES_DAMAGE = 1, 
    GRABBABLE = 2, 
    LIQUID = 4, 
    SOME_OTHER = 8 
}; 

O de forma más legible:

enum 
{ 
    TAKES_DAMAGE = 1 << 0, 
    GRABBABLE = 1 << 1, 
    LIQUID = 1 << 2, 
    SOME_OTHER = 1 << 3 
}; 

¿Por qué? Debido a que usted quiere ser capaz de combinar las banderas sin solapamiento, y también ser capaz de hacer esto:

if(myVar & GRABBABLE) 
{ 
    // grabbable code 
} 

... ¿Qué funciona si los valores de la enumeración siguiente aspecto:

TAKES_DAMAGE: 00000001 
GRABBABLE: 00000010 
LIQUID:  00000100 
SOME_OTHER: 00001000 

Por lo tanto, Supongamos que estableció myVar a GRABBABLE | TAKES_DAMAGE, aquí es como funciona cuando es necesario comprobar el indicador GRABBABLE:

myVar:  00000011 
GRABBABLE: 00000010 [AND] 
------------------- 
      00000010 // non-zero => converts to true 

Si desea configurar myVar-LIQUID | SOME_OTHER, la operación habría dado lugar a:

myVar:  00001100 
GRABBABLE: 00000010 [AND] 
------------------- 
      00000000 // zero => converts to false 
+0

@jldupont - ¿Confío en que quería decir "operador de shiFt"? 8v) –

+0

@jldupont Creo que te falta una carta ... – Greg

+0

No creo haber recibido 5 respuestas simultáneamente. o.o Gracias por la excelente explicación y solución. – quano

4

Debería hacer que las banderas solo tengan potencias de dos, es decir, cada una sea un poco en cualquier tipo de datos en que esté almacenando esto, y nada se solapa cuando realiza operaciones de bits OR.

3

necesita

enum 
{ 
    TAKES_DAMAGE = 1, 
    GRABBABLE = 2, 
    LIQUID = 4, 
    SOME_OTHER = 8 
}; 
+0

Creo que necesita corregir la sintaxis de este. (Lo sé, el mismo error estaba en la pregunta. Eso se ha solucionado ahora, sin embargo). –

+0

@fred larson, corregido :-) – Fredou

4

no puedes establecer los valores de la enumeración?

enum { 
TAKES_DAMAGE = 1, 
GRABBABLE = 2, 
LIQUID  = 4 
} 

Más tarde, simplemente perfom bitwise O en ellos.

5

Sí. En su lugar, hacer que sus miembros de enumeración potencias de 2:

enum 
{ 
    TAKES_DAMAGE = (1 << 0), 
    GRABBABLE = (1 << 1), 
    LIQUID = (1 << 2), 
    SOME_OTHER = (1 << 3) 
}; 
+0

Al infractor: ¿quizás le gustaría explicar lo que encontró que no le resultó útil en esta respuesta? –

25

otra forma de almacenamiento de banderas es la de no molestar con el tipo subyacente en absoluto. cuando se utiliza una enumeración, los valores enum se almacenan de forma predeterminada en un int sin signo, que es de 32 bits en una computadora común. esto le da solo 32 banderas posibles: aunque ciertamente mucho, hay algunos casos en los que no es suficiente.

Ahora puede definir el indicador establecido de esta manera:

typedef struct 
{ 
    int takes_damage : 1; 
    int grabbable : 1; 
    int liquid  : 1; 
    int some_other : 1; 
} flags; 

si nunca se encontró con esto, el '1' parte indica al compilador que utilice sólo 1 bit para almacenar este miembro de estructura.

ahora se puede definir una variable para contener las banderas, y trabajar con esas banderas:

flags myflags = {1,0,0,1}; // defines a variable holding a set of flags, with an initial value of takes_damage & some_other 

myflags.liquid = 1; // change the flags to include the liquid 

if (myflags.takes_damage) // test for one flag 
    apply_damage(); 
if (myflags.liquid && myflags.some_other) // test for multiple flags 
    show_strange_behavior(); 

este método le permite definir cualquier número de banderas, sin limitación, y se puede extender su bandera fijado en cualquier momento sin temor a un desbordamiento. el inconveniente es que probar un subconjunto de las banderas es más engorroso y necesita más código.

+2

Interesante. La primera vez que encuentro este método. Me interesaría leer comentarios de personas más conocedoras que yo acerca de los beneficios e inconvenientes en comparación con el poder tradicional de dos enumeraciones. ¿Funciona con todos los compiladores y todas las arquitecturas? ¿Cuáles son las trampas? – gregschlom

+0

@gregschlom Me gustaría recibir más comentarios también. es totalmente compatible, por lo que debería funcionar con cualquier compilador. las únicas trampas en las que puedo pensar son: 1. si la estructura no está empaquetada, un conjunto de banderas puede requerir una gran cantidad de memoria, mucha de la cual no se usa. 2. No puede especificar múltiples valores de indicador a la vez, como 'myflags = grabbable & liquid' (sin embargo, esto es posible usando una unión que contiene la estructura y un valor int, pero esto impone muchas más restricciones, es más dependiente del compilador y no es el tema de esta publicación) –

+0

¡Gracias! Otro problema (que está relacionado con el hecho de que no se puede probar un subconjunto de las banderas, como se señala), es que no se puede probar fácilmente si dos variables de indicador son iguales. (es decir: no se puede hacer: if (myflags == someOtherFlags) {...}). Un poco de bloqueador para mí, voy a seguir con enumeraciones viejas simples ... – gregschlom