¿Qué hace una mejor constante en C, una macro o una enumeración?

7 minutos de lectura

avatar de usuario
Varun Changani

Estoy confundido acerca de cuándo usar macros o enumeraciones. Ambos se pueden usar como constantes, pero ¿cuál es la diferencia entre ellos y cuál es la ventaja de cualquiera de ellos? ¿Está relacionado de alguna manera con el nivel del compilador o no?

  • Creo que esta es una gran pregunta cuando la consideras así: ¿Cuáles son las diferencias entre usar macros y enumeraciones para definir constantes en C?

    – Sam Harwell

    15 de junio de 2013 a las 16:10

avatar de usuario
Serguéi Kalinichenko

En términos de legibilidad, las enumeraciones son mejores constantes que las macros, porque los valores relacionados se agrupan. Además, enum define un nuevo tipo, por lo que a los lectores de su programa les resultará más fácil averiguar qué se puede pasar al parámetro correspondiente.

Comparar

#define UNKNOWN  0
#define SUNDAY   1
#define MONDAY   2
#define TUESDAY  3
...
#define SATURDAY 7

para

typedef enum {
    UNKNOWN,
    SUNDAY,
    MONDAY,
    TUESDAY,
    ...
    SATURDAY,
} Weekday;

Es mucho más fácil leer código como este.

void calendar_set_weekday(Weekday wd);

que esto

void calendar_set_weekday(int wd);

porque sabe qué constantes está bien pasar.

  • De hecho, pero uno podría quedarse igualmente con las macros, y luego typedef int Weekday; como ni typedefs ni enums son typesafe (en este sentido) en C…

    –Oliver Charlesworth

    15 de junio de 2013 a las 16:14


  • @H2CO3: En gdbYo suelo p (int)Thursdayo p (Weekday)3.

    – jxh

    15 de junio de 2013 a las 16:21

  • @varunchhangani Me gusta enum foo { BAR = 3, BAZ = 1 << 9 }? sí

    –Kevin

    15 de junio de 2013 a las 18:51

  • Por cierto, es legal tener una coma después del último elemento en la lista de enumeración, por lo que el extraño formato de coma no es necesario.

    – jamesdlin

    15 de junio de 2013 a las 18:57

  • @jamesdlin Recientemente descubrí que era no legal en ANSI C. Me sorprendió (siempre pensé que la principal ventaja era que facilitaba un poco la generación de código), pero ahí lo tienen.

    – Tomás

    18 de junio de 2014 a las 7:09


Una macro es una cosa del preprocesador, y el código compilado no tiene idea de los identificadores que crea. Ya han sido reemplazados por el preprocesador antes de que el código llegue al compilador. Una enumeración es una entidad de tiempo de compilación y el código compilado retiene información completa sobre el símbolo, que está disponible en el depurador (y otras herramientas).

Prefiere enumeraciones (cuando puedas).

  • Creo que enum es más cómodo en comparación con macro en caso de depuración.

    – Varun Changani

    15 de junio de 2013 a las 18:47

  • No discutiendo, pero con gcc -g3puede compilar un ejecutable compatible con gdb que conserve información sobre las macros.

    – Braden Mejor

    29 de septiembre de 2015 a las 0:41

avatar de usuario
Kaz

En C, es mejor usar enumeraciones para enumeraciones reales: cuando alguna variable puede contener uno de múltiples valores a los que se les puede dar nombres. Una ventaja de las enumeraciones es que el compilador puede realizar algunas comprobaciones más allá de lo que requiere el lenguaje, como que no falte una declaración de cambio en el tipo de enumeración en uno de los casos. Los identificadores de enumeración también se propagan en la información de depuración. En un depurador, puede ver el nombre del identificador como el valor de una variable de enumeración, en lugar de solo el valor numérico.

Las enumeraciones se pueden usar solo por el efecto secundario de crear constantes simbólicas de tipo integral. Por ejemplo:

enum { buffer_size = 4096 };  /* we don't care about the type */

esta práctica no está tan extendida. Por una cosa, buffer_size se utilizará como un número entero y no como un tipo enumerado. Un depurador no renderizará 4096 dentro buffer_size, porque ese valor no se representará como el tipo enumerado. Si declara algunos char array[max_buffer_size]; luego sizeof array no aparecerá como buffer_size. En esta situación, la constante de enumeración desaparece en tiempo de compilación, por lo que bien podría ser una macro. Y hay desventajas, como no poder controlar su tipo exacto. (Puede haber alguna pequeña ventaja en alguna situación en la que la salida de las etapas de preprocesamiento de la traducción se captura como texto. Una macro se habrá convertido en 4096, mientras que buffer_size se quedará como buffer_size).

Un símbolo de preprocesador nos permite hacer esto:

#define buffer_size 0L /* buffer_size is a long int */

Tenga en cuenta que varios valores de C <limits.h> me gusta UINT_MAX son símbolos de preprocesador y no símbolos de enumeración, por buenas razones, porque esos identificadores deben tener un tipo determinado con precisión. Otra ventaja de un símbolo de preprocesador es que podemos probar su presencia, o incluso tomar decisiones basadas en su valor:

#if ULONG_MAX > UINT_MAX 
/* unsigned long is wider than unsigned int */
#endif

Por supuesto, también podemos probar las constantes enumeradas, pero no de tal manera que podamos cambiar las declaraciones globales en función del resultado.

Las enumeraciones tampoco son adecuadas para las máscaras de bits:

enum modem_control { mc_dsr = 0x1, mc_dtr = 0x2, mc_rts = 0x4, ... }

simplemente no tiene sentido porque cuando los valores se combinan con un OR bit a bit, producen un valor que está fuera del tipo. Dicho código también causa un dolor de cabeza, si alguna vez se transfiere a C++, que tiene (algo más) enumeraciones de tipo seguro.

  • ¿Podemos cambiar el valor de enumeración en tiempo de compilación y const es igual a enumeración o macro?

    – Varun Changani

    15 de junio de 2013 a las 18:53

avatar de usuario
Jens

Tenga en cuenta que existen algunas diferencias entre las macros y las enumeraciones, y cualquiera de estas propiedades puede hacerlas (no) adecuadas como una constante particular.

  • las enumeraciones están firmadas (compatibles con int). En cualquier contexto donde se requiere un tipo sin firmar (¡piense especialmente en operaciones bit a bit!), las enumeraciones están descartadas.
  • si long long es más ancho que int, las constantes grandes no caben en una enumeración.
  • El tamaño de una enumeración es (generalmente) sizeof(int). Para arreglos de valores pequeños (hasta decir, CHAR_MAX) es posible que desee un char foo[] en lugar de un enum foo[] formación.
  • las enumeraciones son números enteros. no puedes tener enum funny_number { PI=3.14, E=2.71 }.
  • las enumeraciones son una característica de C89; Los compiladores de K&R (ciertamente antiguos) no los entienden.

avatar de usuario
Hormiga

Si macro se implementa correctamente (es decir, no sufre problemas de asociatividad cuando se sustituye), entonces no hay mucha diferencia en la aplicabilidad entre macro y constantes de enumeración en situaciones en las que ambas cosas son aplicables, es decir, en una situación en la que necesita constantes enteras con signo específicamente.

Sin embargo, en general, las macros de casos proporcionan una funcionalidad mucho más flexible. Las enumeraciones imponen un tipo específico a sus constantes: tendrán tipo int (o, posiblemente, un tipo de entero con signo más grande), y siempre estarán firmados. Con las macros, puede usar sintaxis constante, sufijos y/o conversiones de tipo explícitas para producir una constante de cualquier tipo.

Las enumeraciones funcionan mejor cuando tiene un grupo de constantes enteras secuenciales estrechamente asociadas. Funcionan especialmente bien cuando no le importan en absoluto los valores reales de las constantes, es decir, cuando solo le importa que tengan algunos valores únicos de buen comportamiento. En todos los demás casos, las macros son una mejor opción (o básicamente la única opción).

Como cuestión práctica, hay poca diferencia. Son igualmente utilizables como constantes en sus programas. Algunos pueden preferir uno u otro por razones estilísticas, pero no puedo pensar en ninguna razón técnica para preferir uno sobre el otro.

Una diferencia es que las macros le permiten controlar el tipo integral de las constantes relacionadas. Pero un enum utilizará un int.

#define X 100L
enum { Y = 100L };

printf("%ld\n", X);
printf("%d\n", Y); /* Y has int type */

avatar de usuario
tarde

enum tiene una ventaja: alcance del bloque:

{ enum { E = 12 }; }
{ enum { E = 13 }; }

Con las macros es necesario #undef.

  • me gustaría Nunca considere dar un enum escriba diferentes valores en diferentes lugares en el mismo código base cualquier tipo de “ventaja” de cualquier manera o forma.

    –Andrew Henle

    21 de febrero a las 12:00

  • Si A ofrece una característica, mientras que B no lo hace, por lo general A tiene una ventaja sobre B.

    – pmor

    22 de febrero a las 18:23

  • También puede dejar caer una bola de bolos en su pie. Eso no es una ventaja.

    –Andrew Henle

    22 de febrero a las 18:27

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad