Por que hace esto:
#include <stdio.h>
#include <limits.h>
#include <inttypes.h>
int main() {
enum en_e {
en_e_foo,
en_e_bar = UINT64_MAX,
};
enum en_e e = en_e_foo;
printf("%zu\n", sizeof en_e_foo);
printf("%zu\n", sizeof en_e_bar);
printf("%zu\n", sizeof e);
}
imprimir 4 8 8
en C y 8 8 8
en C ++ (en una plataforma con entradas de 4 bytes)?
Tenía la impresión de que la UINT64_MAX
asignación forzaría todas las constantes de enumeración a al menos 64 bits, pero en_e_foo
permanece en 32 en C.
¿Cuál es el motivo de la discrepancia?
Respuestas:
En C, una
enum
constante es de tipoint
. En C ++, es del tipo enumerado.enum en_e{ en_e_foo, en_e_bar=UINT64_MAX, };
En C, esto es una violación de la restricción , que requiere un diagnóstico ( si se
UINT64_MAX
excedeINT_MAX
, lo que muy probablemente lo haga). El compilador AC puede rechazar el programa por completo, o puede imprimir una advertencia y luego generar un ejecutable cuyo comportamiento no está definido. (No está 100% claro que un programa que viola una restricción necesariamente tenga un comportamiento indefinido, pero en este caso el estándar no dice cuál es el comportamiento, por lo que sigue siendo un comportamiento indefinido).gcc 6.2 no advierte sobre esto. clang lo hace. Este es un error en gcc; inhibe incorrectamente algunos mensajes de diagnóstico cuando se utilizan macros de encabezados estándar. Gracias a Grzegorz Szpetkowski por localizar el informe de error: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=71613
En C ++, cada tipo de enumeración tiene un tipo subyacente , que es un tipo entero (no necesariamente
int
). Este tipo subyacente debe poder representar todos los valores constantes. Entonces, en este caso, ambosen_e_foo
yen_e_bar
son del tipoen_e
, que debe tener al menos 64 bits de ancho, incluso siint
es más estrecho.fuente
UINT64_MAX
no excederINT_MAX
requiere queint
sea al menos 65 bits.-Wpedantic
y18446744073709551615ULL
pero no conUINT64_MAX
.int
debe ser un tipo con signo, por lo que tendría que tener al menos 65 bits para poder representarUINT64_MAX
(2 ** 64-1).en_e_bar
no es más grande que la enumeración,en_e_foo
es más pequeño. La variable enum era tan grande como la constante más grande.Ese código simplemente no es válido en C en primer lugar.
La sección 6.7.2.2 tanto en C99 como en C11 dice que:
Un diagnóstico del compilador es obligatorio porque es una violación de la restricción, consulte 5.1.1.3:
fuente
En C , mientras que a
enum
se considera un tipo separado, los enumeradores en sí siempre tienen tipoint
.Por lo tanto, el comportamiento que ve es una extensión del compilador.
Yo diría que tiene sentido expandir el tamaño de uno de los enumeradores solo si su valor es demasiado grande.
Por otro lado, en C ++ todos los enumeradores tienen el tipo en el
enum
que están declarados.Por eso, el tamaño de cada enumerador debe ser el mismo. Entonces, el tamaño de todo
enum
se expande para almacenar el enumerador más grande.fuente
Como señalaron otros, el código está mal formado (en C), debido a la violación de la restricción.
Hay un error de GCC # 71613 (informado en junio de 2016), que indica que algunas advertencias útiles se silencian con macros.
La solución actual puede ser anteponer la macro con un
+
operador unario :enum en_e { en_e_foo, en_e_bar = +UINT64_MAX, };
que produce un error de compilación en mi máquina con GCC 4.9.2:
$ gcc -std=c11 -pedantic-errors -Wall main.c main.c: In function ‘main’: main.c:9:20: error: ISO C restricts enumerator values to range of ‘int’ [-Wpedantic] en_e_bar = +UINT64_MAX
fuente
C11 - 6.7.2.2/2
en_e_bar=UINT64_MAX
es una infracción de restricción y esto invalida el código anterior. Se debe producir un mensaje de diagnóstico confirmando la implementación como se indica en el borrador C11:Parece que GCC tiene algún error y no pudo producir el mensaje de diagnóstico. (El error es señalado en la respuesta por Grzegorz Szpetkowski
fuente
sizeof
es un operador en tiempo de compilación. No hay UB aquí, e incluso si lo hubiera, no podría afectarsizeof
.short s = 0xdeadbeef
) y el comportamiento está definido por la implementación.Eché un vistazo a los estándares y mi programa parece ser una violación de restricción en C debido a 6.7.2.2p2 :
y definido en C ++ debido a 7.2.5:
fuente