Cでビッグエンディアンをリトルエンディアンに変換する関数を作成する必要があります。ライブラリ関数は使用できません。
必要なものが単純なバイトスワップであると仮定して、次のようなものを試してください
符号なし16ビット変換:
swapped = (num>>8) | (num<<8);
符号なし32ビット変換:
swapped = ((num>>24)&0xff) | // move byte 3 to byte 0
((num<<8)&0xff0000) | // move byte 1 to byte 2
((num>>8)&0xff00) | // move byte 2 to byte 1
((num<<24)&0xff000000); // byte 0 to byte 3
これにより、バイト順序が位置1234から4321にスワップされます。入力が0xdeadbeef
の場合、32ビットエンディアンスワップの出力は0xefbeadde
になる可能性があります。
上記のコードは、マジックナンバーの代わりにマクロまたは少なくとも定数を使用してクリーンアップする必要がありますが、うまくいけばそのままで役立つことを願っています
編集:別の答えが指摘したように、プラットフォーム、OS、および命令セット固有の代替手段があり、上記よりもはるかに高速になります。 Linuxカーネルには、エンディアンネスを適切に処理するマクロ(cpu_to_be32など)があります。しかし、これらの選択肢は環境に固有のものです。実際には、エンディアンネスは、利用可能なアプローチを組み合わせて使用するのが最適です。
含めることにより:
#include <byteswap.h>
マシン依存のバイトスワップ関数の最適化されたバージョンを取得できます。その後、次の機能を簡単に使用できます。
__bswap_32 (uint32_t input)
または
__bswap_16 (uint16_t input)
#include <stdint.h>
//! Byte swap unsigned short
uint16_t swap_uint16( uint16_t val )
{
return (val << 8) | (val >> 8 );
}
//! Byte swap short
int16_t swap_int16( int16_t val )
{
return (val << 8) | ((val >> 8) & 0xFF);
}
//! Byte swap unsigned int
uint32_t swap_uint32( uint32_t val )
{
val = ((val << 8) & 0xFF00FF00 ) | ((val >> 8) & 0xFF00FF );
return (val << 16) | (val >> 16);
}
//! Byte swap int
int32_t swap_int32( int32_t val )
{
val = ((val << 8) & 0xFF00FF00) | ((val >> 8) & 0xFF00FF );
return (val << 16) | ((val >> 16) & 0xFFFF);
}
更新:64ビットバイトスワッピングを追加
int64_t swap_int64( int64_t val )
{
val = ((val << 8) & 0xFF00FF00FF00FF00ULL ) | ((val >> 8) & 0x00FF00FF00FF00FFULL );
val = ((val << 16) & 0xFFFF0000FFFF0000ULL ) | ((val >> 16) & 0x0000FFFF0000FFFFULL );
return (val << 32) | ((val >> 32) & 0xFFFFFFFFULL);
}
uint64_t swap_uint64( uint64_t val )
{
val = ((val << 8) & 0xFF00FF00FF00FF00ULL ) | ((val >> 8) & 0x00FF00FF00FF00FFULL );
val = ((val << 16) & 0xFFFF0000FFFF0000ULL ) | ((val >> 16) & 0x0000FFFF0000FFFFULL );
return (val << 32) | (val >> 32);
}
これはかなり一般的なバージョンです。私はそれをコンパイルしていないので、おそらくタイプミスがありますが、あなたはアイデアを得る必要があります、
void SwapBytes(void *pv, size_t n)
{
assert(n > 0);
char *p = pv;
size_t lo, hi;
for(lo=0, hi=n-1; hi>lo; lo++, hi--)
{
char tmp=p[lo];
p[lo] = p[hi];
p[hi] = tmp;
}
}
#define SWAP(x) SwapBytes(&x, sizeof(x));
NB:これはnotに最適化されています速度またはスペース。明確(デバッグが容易)で移植性があることを目的としています。
2018-04-04更新コメント者@chuxが発見した、n == 0の無効なケースをトラップするassert()を追加しました。
マクロが必要な場合(組み込みシステムなど):
#define SWAP_UINT16(x) (((x) >> 8) | ((x) << 8))
#define SWAP_UINT32(x) (((x) >> 24) | (((x) & 0x00FF0000) >> 8) | (((x) & 0x0000FF00) << 8) | ((x) << 24))
Edit:これらはライブラリ関数です。それらに従うことは、手動で行う方法です。
__ byteswap_ushort、__ byteswap_ulong、__ byteswap_uint64 を知らない人の数に私は絶対に驚かされます。確かにそれらはVisual C++固有ですが、x86/IA-64アーキテクチャ上でおいしいコードにコンパイルされます。 :)
以下に、bswap
命令の明示的な使用法、 このページから抜粋 を示します。 上記の組み込み形式は常に常にこれよりも高速であることに注意してください。ライブラリルーチンなしで答えます。
uint32 cq_ntohl(uint32 a) {
__asm{
mov eax, a;
bswap eax;
}
}
冗談として:
#include <stdio.h>
int main (int argc, char *argv[])
{
size_t sizeofInt = sizeof (int);
int i;
union
{
int x;
char c[sizeof (int)];
} original, swapped;
original.x = 0x12345678;
for (i = 0; i < sizeofInt; i++)
swapped.c[sizeofInt - i - 1] = original.c[i];
fprintf (stderr, "%x\n", swapped.x);
return 0;
}
4つのint
sの倍数があると仮定して、Intel組み込み関数を使用してSSSE3命令pshufbを使用する方法を次に示します。
unsigned int *bswap(unsigned int *destination, unsigned int *source, int length) {
int i;
__m128i mask = _mm_set_epi8(12, 13, 14, 15, 8, 9, 10, 11, 4, 5, 6, 7, 0, 1, 2, 3);
for (i = 0; i < length; i += 4) {
_mm_storeu_si128((__m128i *)&destination[i],
_mm_shuffle_epi8(_mm_loadu_si128((__m128i *)&source[i]), mask));
}
return destination;
}
これは動作しますか/速くなりますか?
uint32_t swapped, result;
((byte*)&swapped)[0] = ((byte*)&result)[3];
((byte*)&swapped)[1] = ((byte*)&result)[2];
((byte*)&swapped)[2] = ((byte*)&result)[1];
((byte*)&swapped)[3] = ((byte*)&result)[0];
このコードスニペットは、32ビットのリトルエンディアン番号をビッグエンディアン番号に変換できます。
#include <stdio.h>
main(){
unsigned int i = 0xfafbfcfd;
unsigned int j;
j= ((i&0xff000000)>>24)| ((i&0xff0000)>>8) | ((i&0xff00)<<8) | ((i&0xff)<<24);
printf("unsigned int j = %x\n ", j);
}
編集:この関数は、整列された16ビットワードのエンディアンのみを交換します。 UTF-16/UCS-2エンコーディングにしばしば必要な機能。編集終了。
メモリブロックのエンディアンを変更する場合は、非常に高速なアプローチを使用できます。メモリアレイのサイズは8の倍数である必要があります。
#include <stddef.h>
#include <limits.h>
#include <stdint.h>
void ChangeMemEndianness(uint64_t *mem, size_t size)
{
uint64_t m1 = 0xFF00FF00FF00FF00ULL, m2 = m1 >> CHAR_BIT;
size = (size + (sizeof (uint64_t) - 1)) / sizeof (uint64_t);
for(; size; size--, mem++)
*mem = ((*mem & m1) >> CHAR_BIT) | ((*mem & m2) << CHAR_BIT);
}
この種類の関数は、Unicode UCS-2/UTF-16ファイルのエンディアンを変更するのに役立ちます。
私が使用している関数は次のとおりです-テストされ、基本的なデータ型で動作します:
// SwapBytes.h
//
// Function to perform in-place endian conversion of basic types
//
// Usage:
//
// double d;
// SwapBytes(&d, sizeof(d));
//
inline void SwapBytes(void *source, int size)
{
typedef unsigned char TwoBytes[2];
typedef unsigned char FourBytes[4];
typedef unsigned char EightBytes[8];
unsigned char temp;
if(size == 2)
{
TwoBytes *src = (TwoBytes *)source;
temp = (*src)[0];
(*src)[0] = (*src)[1];
(*src)[1] = temp;
return;
}
if(size == 4)
{
FourBytes *src = (FourBytes *)source;
temp = (*src)[0];
(*src)[0] = (*src)[3];
(*src)[3] = temp;
temp = (*src)[1];
(*src)[1] = (*src)[2];
(*src)[2] = temp;
return;
}
if(size == 8)
{
EightBytes *src = (EightBytes *)source;
temp = (*src)[0];
(*src)[0] = (*src)[7];
(*src)[7] = temp;
temp = (*src)[1];
(*src)[1] = (*src)[6];
(*src)[6] = temp;
temp = (*src)[2];
(*src)[2] = (*src)[5];
(*src)[5] = temp;
temp = (*src)[3];
(*src)[3] = (*src)[4];
(*src)[4] = temp;
return;
}
}