Conformidade padrão de uma macro C

8

Eu tenho essa pequena jóia aqui (idéia descaradamente roubada do C-FAQ):

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

É compatível (que não é um comportamento indefinido) com o novo padrão atual (C-18 no momento em que essa pergunta foi feita) e, se sim, quais dos mais antigos também o apoiam?

Também é compatível com C ++ padrão? (Sim, eu sei sobre std::endian)

deamentiaemundi
fonte
Se você está falando questão 10.1 em esta lista C FAQ , recomenda duas técnicas diferentes, significativamente diferentes, que são (AFAIK) ainda é válido.
Steve Summit
Coisas como esta me faz querer sair C ++ ...
snoopy

Respostas:

10

Tem vários problemas:

  • uint32_t não é garantido que exista
  • "ABCD", não é garantido que um array decadente para char*(C) / char const*(C ++) esteja alinhado adequadamente uint32_t*. Se não for, o elenco é UB
  • se o elenco passou, o deref ( *(uint32_t*)"ABCD") é uma violação estrita de alias (UB)

Você pode simplesmente fazer algo assim:

#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á, pode alternar entre qualquer coisa e possui requisitos mínimos de alinhamento.)

Todas as macros, incluindo suas tentativas, têm a desvantagem de serem inadequadas para condicionais de pré-processador ( #if ...) ou em contextos em que é necessária uma expressão constante inteira ( caserótulos, tamanhos de matriz, tamanhos de campo de bits), mas, quando usados ​​em outros lugares, os compiladores modernos geralmente tratam o problema. resultado como uma constante de tempo de compilação no que diz respeito à saída otimizada da montagem.

PSkocik
fonte
Por que, dadas as expressões C11 Standard - 6.5 (p7) (último marcador), você vê uma violação estrita de aliasing para C? O elenco envolvente "a character type"é especificamente permitido?
David C. Rankin
1
@ DavidC.Rankin O OP está convertendo um char*/ char const*para uint32_t*e usando-o para acessar o objeto subjacente. Ele só funciona vice-versa, ou seja, quando você tem um uint32_t*, então você pode acessá-lo através de um ponteiro de caracteres.
PSKocik 03/10/19
1
Fiquei um pouco dividido entre você e @NathanOliver Você respondeu às duas perguntas e, portanto, recebe a marca, mas, no entanto: graças a vocês dois!
Deamentiaemundi 03/10/19
Mas o elenco e a desreferência para uint32_tsi mesmo não são um ponteiro? Geralmente gcc é muito bom em sinalizar um rigoroso apelido violação e digite ponteiros punned, e não há nenhuma queixa uint32_t u = *(uint32_t*)"ABCD";(gcc (GCC) 9.1.0)
David C. Rankin
Alguma preocupação 0x01020304com as plataformas da Middleian que armazenam os bytes 02 01 04 03ou 03 04 01 02?
Eljay
4

Esse não é um comportamento definido no C ++. *(uint32_t*)"ABCD"trata a memória "ABCD"como se fosse uma uint32_t, mas como não é realmente, trata-se de uma violação estrita de alias e um comportamento indefinido.

NathanOliver
fonte