Standardkonformität eines C-Makros

8

Ich habe dieses kleine Juwel hier (Idee schamlos aus den C-FAQ gestohlen):

/* 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 )

Entspricht es dem neuen aktuellen Standard (C-18 zum Zeitpunkt der Beantwortung dieser Frage) (das ist kein undefiniertes Verhalten) und wenn ja, welche der älteren unterstützen es auch?

Ist es auch standardkonformes C ++? (Ja, ich weiß von std::endian)

deamentiaemundi
quelle
Wenn Sie in dieser C-FAQ-Liste über Frage 10.1 sprechen , werden zwei verschiedene, signifikant unterschiedliche Techniken empfohlen, die (AFAIK) noch gültig sind.
Steve Summit
Solche Sachen bringen mich dazu, C ++ zu beenden ...
snoopy

Antworten:

10

Es hat mehrere Probleme:

  • uint32_t ist nicht garantiert zu existieren
  • "ABCD"Es ist nicht garantiert, dass ein Array, das zu einem char*(C) / char const*(C ++) zerfällt, geeignet ausgerichtet ist uint32_t*. Wenn nicht, ist die Besetzung UB
  • Wenn die Besetzung durchgegangen *(uint32_t*)"ABCD"ist , ist deref ( ) eine strikte Aliasing-Verletzung (UB).

Vielleicht möchten Sie stattdessen einfach so etwas tun:

#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

(Funktioniert, weil chares existiert, Alias ​​alles kann und minimale Ausrichtungsanforderungen hat.)

Alle Makros, einschließlich Ihrer Versuche, haben den Nachteil, dass sie für Präprozessorbedingungen ( #if ...) oder in Kontexten ungeeignet sind, in denen ein ganzzahliger konstanter Ausdruck erforderlich ist ( caseBeschriftungen, Arraygrößen, Bitfeldgrößen), aber wenn sie an anderer Stelle verwendet werden, behandeln moderne Compiler im Allgemeinen die Ergebnis als Kompilierungszeitkonstante für die optimierte Baugruppenausgabe.

PSkocik
quelle
Warum sehen Sie bei C11 Standard - 6.5 Ausdrücken (S. 7) (letzter Punkt) eine strikte Aliasing-Verletzung für C? Die Besetzung "a character type"ist ausdrücklich erlaubt?
David C. Rankin
1
@ DavidC.Rankin Das OP wirft ein char*/ char const*to uint32_t*und verwendet dieses dann für den Zugriff auf das zugrunde liegende Objekt. Es funktioniert nur umgekehrt, das heißt, wenn Sie eine haben uint32_t*, dann können Sie es durch einen Zeichenzeiger zuzugreifen.
PSkocik
1
Ich war ein bisschen hin und her gerissen zwischen dir und @NathanOliver Du hast beide Fragen beantwortet und bekommst daher die Note aber trotzdem: Danke an euch beide!
deamentiaemundi
Aber ist die Besetzung und Dereferenzierung zu sich uint32_tselbst nicht ein Zeiger? Im Allgemeinen ist gcc sehr gut darin, eine strikte Alias-Verletzung zu kennzeichnen und punned Zeiger einzugeben, und es gibt keine Beschwerde mit uint32_t u = *(uint32_t*)"ABCD";(gcc (GCC) 9.1.0)
David C. Rankin
Bedenken 0x01020304hinsichtlich Middle-Endian-Plattformen, auf denen die Bytes gespeichert sind, 02 01 04 03oder 03 04 01 02?
Eljay
4

Dies ist kein definiertes Verhalten in C ++. *(uint32_t*)"ABCD"Behandelt die Erinnerung so, "ABCD"als wäre es eine uint32_t, aber da dies nicht wirklich der Fall ist, handelt es sich um eine strikte Aliasing-Verletzung und ein undefiniertes Verhalten.

NathanOliver
quelle