short 値をホスト バイト オーダーからリトル エンディアンに変換する必要があります。ターゲットがビッグ エンディアンの場合、htons() 関数を使用できますが、そうではありません。
私はできると思います:
swap(htons(val))
しかし、これによりバイトが2回スワップされる可能性があり、結果は正しくなりますが、私の場合は問題ないパフォーマンスペナルティが発生します。
short 値をホスト バイト オーダーからリトル エンディアンに変換する必要があります。ターゲットがビッグ エンディアンの場合、htons() 関数を使用できますが、そうではありません。
私はできると思います:
swap(htons(val))
しかし、これによりバイトが2回スワップされる可能性があり、結果は正しくなりますが、私の場合は問題ないパフォーマンスペナルティが発生します。
エンディアンとIBMからのエンディアンの判別方法に関する記事は次のとおりです。
Cでエンディアンに依存しないコードを書く:エンディアンを「バイト」にしないでください
実行時にエンディアンを決定する方法の例が含まれています(これは1回だけ実行する必要があります)
const int i = 1;
#define is_bigendian() ( (*(char*)&i) == 0 )
int main(void) {
int val;
char *ptr;
ptr = (char*) &val;
val = 0x12345678;
if (is_bigendian()) {
printf(“%X.%X.%X.%X\n", u.c[0], u.c[1], u.c[2], u.c[3]);
} else {
printf(“%X.%X.%X.%X\n", u.c[3], u.c[2], u.c[1], u.c[0]);
}
exit(0);
}
このページには、バイト順序を逆にする方法に関するセクションもあります。
short reverseShort (short s) {
unsigned char c1, c2;
if (is_bigendian()) {
return s;
} else {
c1 = s & 255;
c2 = (s >> 8) & 255;
return (c1 << 8) + c2;
}
}
;
short reverseShort (char *c) {
short s;
char *p = (char *)&s;
if (is_bigendian()) {
p[0] = c[0];
p[1] = c[1];
} else {
p[0] = c[1];
p[1] = c[0];
}
return s;
}
次に、エンディアンを知り、条件付きで htons() を呼び出す必要があります。実際には、hton でさえありませんが、条件付きでバイトを交換するだけです。もちろん、コンパイル時。
次のようなもの:
unsigned short swaps( unsigned short val)
{
return ((val & 0xff) << 8) | ((val & 0xff00) >> 8);
}
/* host to little endian */
#define PLATFORM_IS_BIG_ENDIAN 1
#if PLATFORM_IS_LITTLE_ENDIAN
unsigned short htoles( unsigned short val)
{
/* no-op on a little endian platform */
return val;
}
#elif PLATFORM_IS_BIG_ENDIAN
unsigned short htoles( unsigned short val)
{
/* need to swap bytes on a big endian platform */
return swaps( val);
}
#else
unsigned short htoles( unsigned short val)
{
/* the platform hasn't been properly configured for the */
/* preprocessor to know if it's little or big endian */
/* use potentially less-performant, but always works option */
return swaps( htons(val));
}
#endif
適切に構成されたシステムがある場合 (プリプロセッサがターゲット ID がリトル エンディアンかビッグ エンディアンかを認識するように)、「最適化された」バージョンのhtoles()
. そうしないと、に依存する最適化されていない可能性のあるバージョンが取得されhtons()
ます。いずれにせよ、あなたはうまくいくものを手に入れます。
あまりトリッキーではなく、多かれ少なかれ移植性があります。
もちろん、これを適切なマクロと一緒に、inline
またはマクロとして実装することで、最適化の可能性をさらに向上させることができます。
さまざまなコンパイラのエンディアンを定義する実際の実装については、「Portable Open Source Harness (POSH)」のようなものを見たいと思うかもしれません。ライブラリにアクセスするには、疑似認証ページを通過する必要があることに注意してください (ただし、個人情報を提供するために登録する必要はありません): http://hookatooka.com/poshlib/
私の経験則によるパフォーマンスの推測は、データの大きなブロックを一度にリトル エンディアン化するか、1 つの値だけにするかによって異なります。
値が 1 つだけの場合、コンパイラが不要なバイト スワップを最適化しない場合でも、関数呼び出しのオーバーヘッドが不要なバイト スワップのオーバーヘッドを圧倒する可能性があります。次に、値をソケット接続のポート番号として書き込み、ソケットを開くかバインドしようとしますが、これはあらゆる種類のビット操作に比べて時間がかかります。だから心配しないでください。
大きなブロックの場合、コンパイラがそれを処理しないのではないかと心配するかもしれません。したがって、次のようにします。
if (!is_little_endian()) {
for (int i = 0; i < size; ++i) {
vals[i] = swap_short(vals[i]);
}
}
または、かなり高速に実行できるアーキテクチャの SIMD 命令を調べてください。
is_little_endian()
好きなトリックを使って書いてください。Robert S. Barnes が提供するものは健全だと思いますが、通常、特定のターゲットがビッグエンディアンになるかリトルエンディアンになるかはわかっているので、プラットフォーム固有のヘッダー ファイルを用意して、それを1 または 0 のいずれかに評価されるマクロ。
いつものように、パフォーマンスが本当に気になる場合は、生成されたアセンブリを見て、無意味なコードが削除されているかどうかを確認し、さまざまな代替案を相互に比較して、実際に何が最速かを確認してください。
残念ながら、標準 C を使用してコンパイル時にシステムのバイト オーダーを決定するためのクロスプラットフォームの方法は実際にはありませ#define
んconfig.h
。
LITTLE_ENDIAN
orの正しい定義を確認する単体テストは次のBIG_ENDIAN
ようになります。
#include <assert.h>
#include <limits.h>
#include <stdint.h>
void check_bits_per_byte(void)
{ assert(CHAR_BIT == 8); }
void check_sizeof_uint32(void)
{ assert(sizeof (uint32_t) == 4); }
void check_byte_order(void)
{
static const union { unsigned char bytes[4]; uint32_t value; } byte_order =
{ { 1, 2, 3, 4 } };
static const uint32_t little_endian = 0x04030201ul;
static const uint32_t big_endian = 0x01020304ul;
#ifdef LITTLE_ENDIAN
assert(byte_order.value == little_endian);
#endif
#ifdef BIG_ENDIAN
assert(byte_order.value == big_endian);
#endif
#if !defined LITTLE_ENDIAN && !defined BIG_ENDIAN
assert(!"byte order unknown or unsupported");
#endif
}
int main(void)
{
check_bits_per_byte();
check_sizeof_uint32();
check_byte_order();
}
このトリックは、起動時にntohs
ダミー値を使用して、結果の値を元の値と比較する必要があります。両方の値が同じ場合、マシンはビッグ エンディアンを使用し、それ以外の場合はリトル エンディアンを使用します。
次に、最初のテストの結果に応じて、ToLittleEndian
何もしないか、または を呼び出すメソッドを使用します。ntohs
(コメントで提供された情報で編集)
多くの Linux システムには、変換機能を備え た<endian.h>
またはがあります。ENDIAN(3) の man ページ<sys/endian.h>