Cumplimiento estándar de una macro C

8

Tengo esta pequeña joya aquí (idea robada descaradamente de las preguntas frecuentes de C):

/* A lot of checks omitted to get rid of the architectures with a "weird" endianness */
/*...*/
#define MP_ENDIANESS ( (0x41424344ul == *(uint32_t*)"ABCD") ? MP_BIG_ENDIAN : MP_LITTLE_ENDIAN )

¿Cumple (no es un comportamiento indefinido) con el nuevo estándar actual (C-18 en el momento en que se hizo esta pregunta) y, en caso afirmativo, cuál de los más antiguos también lo admite?

¿Es también estándar compatible con C ++? (Sí, lo sé std::endian)

deamentiaemundi
fuente
Si está hablando de la pregunta 10.1 en esta lista de preguntas frecuentes de C , recomienda dos técnicas diferentes y significativamente diferentes que son (AFAIK) aún válidas.
Steve Summit
Cosas como esta me dan ganas de dejar C ++ ...
snoopy

Respuestas:

10

Tiene varios problemas:

  • uint32_t no se garantiza que exista
  • "ABCD", no se garantiza que una matriz en descomposición a char*(C) / char const*(C ++) esté alineada adecuadamente uint32_t*. Si no es así, el elenco es UB
  • si el reparto pasó, el deref ( *(uint32_t*)"ABCD") es una violación de alias estricta (UB)

Es posible que desee hacer algo como esto en su lugar:

#if !__cplusplus
    #define LITTLE_ENDIAN_EH() (*(char*)&(int){1});
#else
    //C++ doesn't have compound literals
    static int const LITTLE_ENDIAN_EH_ = 1;
    #define LITTLE_ENDIAN_EH() (*(char*)&LITTLE_ENDIAN_EH_)
#endif

(Funciona porque charexistirá, puede alias cualquier cosa y tiene requisitos mínimos de alineación).

Todas las macros, incluidos sus intentos, tienen la desventaja de ser inadecuadas para los condicionales del preprocesador ( #if ...) o en contextos donde se requiere una expresión constante entera ( caseetiquetas, tamaños de matriz, tamaños de campo de bits), pero cuando se usan en otros lugares, los compiladores modernos generalmente tratan el resultado como una constante de tiempo de compilación en lo que respecta a la salida de ensamblaje optimizada

PSkocik
fuente
¿Por qué se le da el Estándar C11 - 6.5 Expresiones (p7) (última viñeta) ¿ve una violación estricta de alias para C? ¿El elenco involucrado "a character type"está específicamente permitido?
David C. Rankin el
1
@ DavidC.Rankin El OP está lanzando un char*/ char const*a uint32_t*y luego lo usa para acceder al objeto subyacente. Sólo funciona al revés, es decir, cuando tiene una uint32_t*, entonces se puede acceder a él a través de un puntero de carácter.
PSkocik
1
Estaba un poco dividido entre usted y @NathanOliver. Respondió a ambas preguntas y, por lo tanto, obtiene la marca, pero de todos modos: ¡gracias a los dos!
deamentiaemundi
Pero, ¿no es el reparto y la desreferencia hacia uint32_tsí mismo y no un puntero? En general, gcc es muy bueno para señalar una violación estricta de alias y escribir punteros punteados, y no hay ninguna queja con uint32_t u = *(uint32_t*)"ABCD";(gcc (GCC) 9.1.0)
David C. Rankin el
¿Alguna preocupación para 0x01020304las plataformas intermedias que almacenan los bytes 02 01 04 03o 03 04 01 02?
Eljay
4

Este no es un comportamiento definido en C ++. *(uint32_t*)"ABCD"trata la memoria de "ABCD"como si fuera un uint32_t, pero como no lo es realmente, esta es una violación de alias estricta y un comportamiento indefinido.

NathanOliver
fuente