Cマクロの標準準拠


8

私はここにこの小さな宝石を持っています(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 )

それは新しい現在の標準(この質問が尋ねられたときのC-18)に準拠しているか(未定義の動作ではありません)、準拠している場合は、古いバージョンのどれもそれをサポートしていますか?

また、標準に準拠したC ++ですか?(はい、知っていますstd::endian


あなたが話をしている場合は問題と10.1、このC FAQリスト、それはまだ有効な(私の知る限り)2つの異なる、大幅に異なる技術を推奨しています。
Steve Summit

このようなことで、C ++を終了したいと思います...
snoopy

回答:


10

それにはいくつかの問題があります:

  • uint32_t 存在することが保証されていません
  • "ABCD"char*(C)/ char const*(C ++)に減衰する配列は、に対して適切に配置されることが保証されていませんuint32_t*。そうでない場合、キャストはUBです。
  • キャストが通過した場合、deref(*(uint32_t*)"ABCD")は厳密なエイリアス違反(UB)です。

代わりに、次のようなことを単純に実行できます。

#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

char存在するので機能し、何でもエイリアスでき、最小限の配置要件があります。)

あなたの試みを含むすべてのマクロには、プリプロセッサ条件(#if ...)または整数定数式が必要なコンテキスト(caseラベル、配列サイズ、ビットフィールドサイズ)には適さないという欠点がありますが、他の場所で使用する場合、最新のコンパイラーは通常最適化されたアセンブリ出力に関する限り、コンパイル時定数としての結果。


C11標準-6.5式(p7)(最後の箇条書き)が与えられているのに、Cの厳密なエイリアス違反が見られるのはなぜですか?関与するキャスト"a character type"は特に許可されていますか?
David

1
@ DavidC.Rankin OPはchar*/ char const*にキャストしuint32_t*、それを使用して、基になるオブジェクトにアクセスします。それは、その逆の作品だけが持っているとき、すなわちuint32_t*その後、あなたは文字ポインタを介してアクセスすることができます。
PSkocik

1
私はあなたと@NathanOliverの間で少し引き裂かれました
deamentiaemundi

しかし、キャストではなく、uint32_tそれ自体への間接参照ではなく、ポインタではありませんか?一般的にgccが、厳密なエイリアス違反にフラグを立てるには非常に良いですし、punnedポインタを入力して、との苦情がないuint32_t u = *(uint32_t*)"ABCD";(GCC(GCC)9.1.0は)
デビッド・C.ランキン

0x01020304バイトを格納するミドルエンディアンプラットフォームに関する懸念、02 01 04 03または03 04 01 02
Eljay

4

これはC ++では定義された動作ではありません。 *(uint32_t*)"ABCD"のメモリをの"ABCD"ように扱いますが、uint32_t実際にはそうではないため、これは厳密なエイリアシング違反であり、未定義の動作です。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.