私が書いたオープンソースプログラムでは、ファイルから(別のプログラムによって書かれた)バイナリデータを読み取り、int、double、およびその他のさまざまなデータ型を出力しています。課題の1つは、両方のエンドネスの32ビットおよび64ビットマシンで実行する必要があることです。つまり、かなりの低レベルのビットをいじる必要があります。私は型のパンニングと厳密なエイリアシングについて(非常に)少し知っているので、正しい方法で物事を行っていることを確認したいと思います。
基本的に、char*からさまざまなサイズのintに変換するのは簡単です。
int64_t snativeint64_t(const char *buf)
{
/* Interpret the first 8 bytes of buf as a 64-bit int */
return *(int64_t *) buf;
}
そして、必要に応じて次のようなバイト順序を交換するためのサポート関数のキャストがあります。
int64_t swappedint64_t(const int64_t wrongend)
{
/* Change the endianness of a 64-bit integer */
return (((wrongend & 0xff00000000000000LL) >> 56) |
((wrongend & 0x00ff000000000000LL) >> 40) |
((wrongend & 0x0000ff0000000000LL) >> 24) |
((wrongend & 0x000000ff00000000LL) >> 8) |
((wrongend & 0x00000000ff000000LL) << 8) |
((wrongend & 0x0000000000ff0000LL) << 24) |
((wrongend & 0x000000000000ff00LL) << 40) |
((wrongend & 0x00000000000000ffLL) << 56));
}
実行時に、プログラムはマシンのエンディアンを検出し、上記のいずれかを関数ポインターに割り当てます。
int64_t (*slittleint64_t)(const char *);
if(littleendian) {
slittleint64_t = snativeint64_t;
} else {
slittleint64_t = sswappedint64_t;
}
さて、char *をdoubleにキャストしようとすると、トリッキーな部分が発生します。次のようにエンディアンスワッピングコードを再利用したいと思います。
union
{
double d;
int64_t i;
} int64todouble;
int64todouble.i = slittleint64_t(bufoffset);
printf("%lf", int64todouble.d);
ただし、一部のコンパイラは、「int64todouble.i」割り当てを最適化してプログラムを中断する可能性があります。このプログラムはパフォーマンスのために最適化されたままでなければならず、char *を直接doubleにキャストするための並列変換セットを記述したくないことを考慮しながら、これを行うためのより安全な方法はありますか?駄洒落の結合方法が安全である場合、それを使用するためにsnativeint64_tのような関数を書き直す必要がありますか?
変換関数がmemcpyを使用するように書き直されたため、SteveJessopの回答を使用することになりました。
int64_t snativeint64_t(const char *buf)
{
/* Interpret the first 8 bytes of buf as a 64-bit int */
int64_t output;
memcpy(&output, buf, 8);
return output;
}
私の元のコードとまったく同じアセンブラーにコンパイルされました:
snativeint64_t:
movq (%rdi), %rax
ret
2つのうち、memcpyバージョンは、私がやろうとしていることをより明確に表現しており、最も単純なコンパイラーでも機能するはずです。
アダム、あなたの答えも素晴らしかったし、私はそれから多くを学びました。投稿してくれてありがとう!