code - typedef enum c ejemplo
¿Por qué dos constantes enumerativas de enum diferentes tienen el mismo valor entero? (5)
Sé que si definí un día de semana enum como este:
enum weekday {
MON,
TUE,
WED,
THU,
FRI,
};
Entonces, MON internamente equivaldría a 0, por defecto, y TUE a 1, WED a 2 ...
Pero si lo defino de esta manera:
enum weekday {
MON,
TUE = 0,
WED,
THU,
FRI,
};
Entonces tanto MON
como TUE
obtendrían el valor de 0.
¿Cómo podría un sistema diferenciar MON y TUE internamente? Quiero decir, si declaro algo como esto:
enum weekday today = 0;
Entonces, ¿hoy es MON
o TUE
? O, filosóficamente hablando, ¿ambas cosas?
El nombre de la constante de enumeración se usa para asignar el valor y no el valor real en sí mismo. Si asigna el valor 0 a hoy, el valor de salida será 0. Y sí, tanto MON como TUE tendrán el valor 0 y el resto asignará el valor como WED = 1 THU = 2 y así sucesivamente.
Es tan filosófico (o no) como
#define ZILCH 0
#define NADA 0
Hay muchos usos en los que tiene sentido que diferentes nombres den como resultado el mismo número.
Las enumeraciones son números enteros "realmente", no solo porque se implementan de esa manera, sino porque el estándar define que los tipos enum tienen valores enteros. Entonces, el valor de today
es "realmente" 0. Todo lo que ha sucedido es que ha creado dos nombres diferentes para el valor 0.
Supongo que la respuesta a "hoy es MON o TUE" es "sí" ;-)
El lenguaje no lo detiene porque de vez en cuando es útil que una enumeración tenga varios nombres para el mismo valor. Por ejemplo:
enum compression_method {
COMP_NONE = 0,
COMP_LOW = 1,
COMP_HIGH = 2,
COMP_BEST = 2,
COMP_FASTEST = 0,
};
Solo para complementar otras respuestas, le daré un ejemplo práctico de cómo usar el mismo valor para diferentes enumeraciones en una enum
dada es ampliamente útil:
enum slots_t {
SLOT_FIRST = 0,
SLOT_LEFTARM = SLOT_FIRST,
SLOT_RIGHTARM = 1,
SLOT_TORSO = 2,
SLOT_LEFTLEG = 3,
SLOT_RIGHTLEG = 4,
SLOT_LAST = SLOT_RIGHTLEG
};
Entonces puedes hacer en tu código:
for (int i = SLOT_FIRST; i <= SLOT_LAST; ++i) { }
¿Por qué dos constantes de enumeración diferentes tienen el mismo valor entero?
Porque está explícitamente permitido por el borrador estándar N1265 C99 en 6.7.2.2/3 "Especificadores de enumeración":
El uso de enumeradores con
=
puede producir constantes de enumeración con valores que duplican otros valores en la misma enumeración.
¿Cómo podría un sistema diferenciar MON y TUE internamente?
Creo que es imposible porque son constantes de tiempo de compilación (6.6 / 6 "expresiones constantes"). Como consecuencia ellos:
no se puede modificar para que difieran después de la compilación
no tienen una dirección que los distinga: la ubicación de la memoria del valor enum en C
Las constantes de tiempo de compilación no necesitan ninguna dirección porque las direcciones son inútiles para cosas que no se pueden modificar.
GCC simplemente reemplaza el uso de los miembros enum con valores inmediatos en el ensamblado en tiempo de compilación. Considerar:
#include <stdio.h>
enum E {
E0 = 0x1234,
E1 = 0x1234
};
int i = 0x5678;
int main() {
printf("%d/n", E0);
printf("%d/n", E1);
printf("%d/n", i);
return 0;
}
Compile y descompile con GCC 4.8 x86_64:
gcc -c -g -O0 -std=c89 main.c
objdump -Sr main.o
La salida contiene:
printf("%d/n", E0);
4: be 34 12 00 00 mov $0x1234,%esi
...
printf("%d/n", E1);
18: be 34 12 00 00 mov $0x1234,%esi
...
printf("%d/n", i);
2c: 8b 05 00 00 00 00 mov 0x0(%rip),%eax # 32 <main+0x32>
2e: R_X86_64_PC32 i-0x4
32: 89 c6 mov %eax,%esi
Entonces vemos que:
- los miembros enum se usan como inmediatos
$0x1234
, por lo que es imposible saber de dónde vinieron - la variable
i
sin embargo, proviene de la memoria0x0(%rip)
(para ser reubicada), por lo que dos variables pueden ser diferenciadas por dirección