c++ - ¿Por qué las implementaciones de "stdint.h" no están de acuerdo con la definición de UINT8_C?
standards integer-promotion (3)
La macro
UINT8_C
se define en "stdint.h", con la siguiente
specification
:
La macro
UINTN_C(value)
se expandirá a una expresión constante entera correspondiente al tipo
uint_leastN_t
.
En la naturaleza, sin embargo, las implementaciones difieren:
#define UINT8_C(value) ((uint8_t) __CONCAT(value, U)) // AVR-libc
#define UINT8_C(x_) (static_cast<std::uint8_t>(x_)) // QP/C++
#define UINT8_C(c) c // GNU C Library
Las dos primeras implementaciones parecen más o menos equivalentes, pero la tercera se comporta de manera diferente: por ejemplo, el siguiente programa imprime
1
con AVR-libc y QP / C ++, pero
-1
con glibc (porque los cambios a la derecha en los valores con signo propagan el bit de signo).
std::cout << (UINT8_C(-1) >> 7) << std::endl; // prints -1 in glibc
La implementación de
UINT16_C
muestra el mismo comportamiento, pero no
UINT32_C
, porque su definición incluye el sufijo
U
:
#define UINT32_C(c) c ## U
Curiosamente, la definición de glibc de
UINT8_C
cambió
en 2006
, debido a
un informe de error
.
La definición anterior era
#define UINT8_C(c) c ## U
, pero eso produjo un resultado incorrecto (
false
) en
-1 < UINT8_C(0)
debido a reglas de promoción de enteros.
¿Son correctas las tres definiciones según el estándar? ¿Existen otras diferencias (además del manejo de constantes negativas) entre estas tres implementaciones?
La biblioteca GNU C no es correcta.
Según
C11 7.20.4.1 Macros para constantes enteras de ancho mínimo
UINTN_C(value)
se define como
La macro
UINTN_C(value)
se expandirá a una expresión constante entera correspondiente al tipouint_leastN_t
.
Por lo tanto, no es apropiado, solo usan
c
ya que
c
puede o no ser
uint_least8_t
.
Las dos primeras implementaciones no se ajustan al estándar C, porque no permiten
UINT8_C(42)
en
#if
directivas
#if
:
#if UINT8_C(42) == 42 // <- should be a valid expression
3 :
Cada invocación de una de estas macros se expandirá a una expresión constante entera adecuada para su uso en
#if
directivas de preprocesamiento. El tipo de la expresión tendrá el mismo tipo que una expresión del tipo correspondiente convertida de acuerdo con las promociones de enteros. El valor de la expresión será el del argumento.
Si un
int
puede representar todos los valores de un
uint_least8_t
entonces la implementación de GNU de la
UINT8_C(value)
como
#define UINT8_C(c) c
ajusta al estándar C.
Según C11 7.20.4 Macros para las constantes enteras párrafo 2 :
El argumento en cualquier caso de estas macros debe ser una constante entera sin sufijo (como se define en 6.4.4.1 ) con un valor que no exceda los límites para el tipo correspondiente.
Por ejemplo, si
UINT_LEAST8_MAX
es 255, los siguientes ejemplos de uso son legales:
-
UINT8_C(0)
-
UINT8_C(255)
-
UINT8_C(0377)
-
UINT8_C(0xff)
Pero los siguientes ejemplos de uso resultan en un comportamiento indefinido :
-
UINT8_C(-1)
: no es una constante entera como se define en 6.4.4.1 -
UINT8_C(1u)
: no es una constante entera sin mezclar -
UINT8_C(256)
: supera los límites deuint_least8_t
para esta implementación
El equivalente
INT8_C(-1)
también es
un comportamiento indefinido
por los mismos motivos.
Si
UINT_LEAST8_MAX
es 255, una instancia legal de
UINT8_C(value)
se expandirá a una expresión constante entera y su tipo será
int
debido a promociones enteras, según el párrafo
3
:
Cada invocación de una de estas macros se expandirá a una expresión constante entera adecuada para su uso en
#if
directivas de preprocesamiento. El tipo de la expresión tendrá el mismo tipo que una expresión del tipo correspondiente convertida de acuerdo con las promociones de enteros. El valor de la expresión será el del argumento.
Por lo tanto, para cualquier invocación legal de
UINT8_C(value)
, la expansión de esto a
value
por cualquier implementación donde un
int
puede representar todos los valores de
uint_least8_t
es perfectamente estándar.
Para cualquier invocación ilegal de
UINT8_C(value)
es posible que no obtenga el resultado que esperaba debido a
un comportamiento indefinido
.
[EDITAR para completar] Como se señaló en la
answer
cpplearner
, las otras implementaciones de
UINT8_C(value)
muestran en la pregunta de OP no son válidas porque se expanden a expresiones que no son adecuadas para su uso en directivas de procesamiento
#if
.