c++ c standards integer-promotion stdint

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 tipo uint_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 de uint_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 .