Publi

Flags en un enum en C


Las flags, son opciones binarias que acompañan un dato o un sistema y valen para informar o definir su comportamiento o su estado entre otras cosas.

En muchos casos, utilizaremos flags para ver el resultado de una operación, imaginémonos que estamos pasando una serie de tests a un dato de texto:

  1. El dato no está vacío
  2. El dato tiene más de 3 letras
  3. El dato tiene menos de 20 letras
  4. El dato tiene caracteres no numéricos
  5. El dato tiene caracteres no números/letras
  6. El dato tiene caracteres de control
  7. El dato tiene caracteres no válidos
  8. El dato tiene formato de fecha
  9. El dato tiene formato de hora
  10. El dato tiene formato de teléfono
  11. El dato tiene formato de coordenadas

Bueno, así puedo poner muchas condiciones, lo importante es la forma de almacenarlo. Si usamos un array de enteros, estaremos ocupando 11 valores multplicado por 4 o por 8, resultan 44 u 88 bytes para almacenar sólo valores booleanos. Cuando podemos almacenarlo todo en un entero manipulando la variable a nivel de bit, definiendo el primer bit para la primera opción, el segundo para la segunda y así sucesivamente.

Tenemos que tener en cuenta el tamaño de la variable (la cantidad de bit que podemos meter). De esta forma ahorraremos espacio en memoria (imaginemos que tenemos un array o una lista de muchísimos elementos, y cada uno tiene sus flags) y tiempo en la ejecución y en la compilación (a nivel de CPU se puede optimizar mucho).

Un ejemplo que habrán visto especialmente las personas que trabajan con sistemas *nix son los permisos de los archivos, en los que vemos 3 grupos de 3 flags (lectura, escritura y ejecución. Si nos quedamos con un solo grupo, vemos que podemos darle valores del 0 al 7, por lo que:

  • sin permiso = 0
  • lectura (read) = 4
  • escritura (write) = 2
  • ejecución (execute) = 1

Y también podemos hacer combinaciones (lectura + escritura = 6), (escritura + ejecucón = 3), (lectura + escritura + ejecución = 7).
Aunque, ¿ por qué esos números tan curiosos que nos gustan tanto a los informáticos ? 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384… (vamos, todos potencia de 2 (20 = 1, 21 = 2, 22 = 4, 23 = 8, 24 = 16…) y la gracia está en que si lo expresamos en binario nos queda:

  • ejecución (execute) = 0001
  • escritura (write) = 0010
  • lectura (read) = 0100

Por lo que vamos moviendo el bit de sitio dependiendo de lo que queramos, es más, podemos jugar poniendo y sacando bits dependiendo de las posiciones (lectura + escritura = 0110), (escritura + ejecucion = 0011), (lectura + escritura + ejecución = 0111), vamos al final todo coincide.

Pero claro, no podemos depender de acordarnos de la posición de un determinado bit a la hora de establecer las flags, leerlas y escribirlas, cuando programamos, siempre intentamos dar nombre a las cosas, porque un pequeño texto es siempre más fácil de recordar y evita errores (bit arriba, bit abajo, cuando tenemos la pantalla llena es fácil despistarse.

Por un lado, podemos utilizar #define, tantos como valores tengamos, no ocupa memoria, pero se nos pueden juntar muchas declaraciones, debemos comentar todo muy bien para poder mantener el código.
Podemos utilizar constantes, pero tenemos el mismo problema de antes, pero ahora sí que ocupan memoria, y cada constante ocupará como mínimo 1 byte (si las declaramos como short o como char, o incluso más, porque los valores que adquirirán estas variables desbordarían rápidamente los tipos más pequeños).
Otra opción es utilizar enum, definiendo los valores de los distintos elementos del tipo enumerado, de la siguiente manera:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include <stdio.h>

enum TPermisos
  {
    PERM_NINGUNO = 0,
      PERM_LECTURA = 1,
      PERM_ESCRITURA = 1 << 1,
      PERM_CREACION = 1 << 2,
      PERM_BORRADO = 1 << 3,
      PERM_MODIFICAR_OTROS = 1 << 4,
      PERM_TODOS =  ~0
  };

int main()
{
  printf ("PERM_NINGUNO: %d\n", PERM_NINGUNO);
  printf ("PERM_LECTURA: %d\n", PERM_LECTURA);
  printf ("PERM_ESCRITURA: %d\n", PERM_ESCRITURA);
  printf ("PERM_CREACION: %d\n", PERM_CREACION);
  printf ("PERM_BORRADO: %d\n", PERM_BORRADO);
  printf ("PERM_MODIFICAR_OTROS: %d\n", PERM_MODIFICAR_OTROS);
  printf ("PERM_TODOS: %u\n", PERM_TODOS);
// Comprobación de valores
  unsigned valor = PERM_ESCRITURA | PERM_CREACION | PERM_LECTURA;
  printf ("LECTURA ? %d\n", valor & PERM_LECTURA);
  printf ("CREACION ? %d\n", valor & PERM_CREACION);
  printf ("BORRADO ? %d\n", valor & PERM_BORRADO);

  return 0;
}

Aquí se muestran los valores de todos los permisos que hemos puesto en nuestro sistema. Como dato importante utilizamos los operadores binarios (::CODECOLORER_BLOCK_2::

Para decirle expresamente que utilice un unsigned para almacenar la información.

Para completar un poco la información, hace mucho tiempo escribí algunos artículos sobre manipulación de bits que pueden ser interesantes: Bailando con bits, Bailando con bits II.
Foto: (Flickr) CC-by

También podría interesarte....

There are 4 comments left Ir a comentario

  1. Pingback: BlogESfera.com /

  2. Pingback: Flags en un enum en C | PlanetaLibre /

  3. Pingback: REISUB, ¿qué es? ¿para qué sirve? ¿cómo hacerlo de forma remota o en script? y más llamadas a Alt+Sysrq – Poesía Binaria /

  4. Usando Google Chrome Google Chrome 122.0.0.0 en Windows Windows NT

    This is excellent article, thank you for the share! This is what I am looking for, hope in future you will continue sharing such an superb work.

Leave a Reply