配列sizeof(int)
からバイトを読み取りたい。char*
a)エンディアンをチェックする必要がある場合、どのシナリオで心配する必要がありますか?
b)エンディアンを考慮しているかどうかにかかわらず、最初の4バイトをどのように読み取りますか。
編集:私が読んだバイトはsizeof(int)
整数値と比較する必要があります。
この問題に取り組むための最良のアプローチは何ですか
配列sizeof(int)
からバイトを読み取りたい。char*
a)エンディアンをチェックする必要がある場合、どのシナリオで心配する必要がありますか?
b)エンディアンを考慮しているかどうかにかかわらず、最初の4バイトをどのように読み取りますか。
編集:私が読んだバイトはsizeof(int)
整数値と比較する必要があります。
この問題に取り組むための最良のアプローチは何ですか
そのようなことを意味しますか?:
char* a;
int i;
memcpy(&i, a, sizeof(i));
データのソースがデバイスなどの別のプラットフォームからのものである場合にのみ、エンディアンについて心配する必要があります。
a) データがビッグ エンディアン マシンで作成され、リトル エンディアン マシンで処理されている場合、またはその逆の場合にのみ、「エンディアン」 (つまり、バイト スワッピング) について心配する必要があります。これには多くの方法がありますが、ここではいくつかの例を示します。
どちらの場合でも、1 バイトより大きいすべての数値 (short、int、long、double など) をバイト スワップする必要があります。ただし、常に同じプラットフォームのデータを扱う場合は、endian問題はありません。
b) あなたの質問に基づいて、char ポインターがあり、最初の 4 バイトを int として抽出し、エンディアンの問題に対処したいようです。抽出を行うには、これを使用します。
int n = *(reinterpret_cast<int *>(myArray)); // where myArray is your data
明らかに、これは myArray が null ポインターではないことを前提としています。そうしないと、ポインターを逆参照するためクラッシュするため、防御的なプログラミング スキームを使用してください。
Windows でバイトを交換するには、winsock2.h で定義されている ntohs()/ntohl() および/または htons()/htonl() 関数を使用できます。または、C++ でこれを行う単純なルーチンをいくつか作成することもできます。たとえば、次のようになります。
inline unsigned short swap_16bit(unsigned short us)
{
return (unsigned short)(((us & 0xFF00) >> 8) |
((us & 0x00FF) << 8));
}
inline unsigned long swap_32bit(unsigned long ul)
{
return (unsigned long)(((ul & 0xFF000000) >> 24) |
((ul & 0x00FF0000) >> 8) |
((ul & 0x0000FF00) << 8) |
((ul & 0x000000FF) << 24));
}
それらをどのように読みたいかによって異なりますが、4 バイトを整数にキャストしたいような気がします。ネットワーク ストリーミング データを介してキャストすると、通常は次のようになります。
int foo = *(int*)(stream+offset_in_stream);
これを解決する簡単な方法は、バイトを生成するものは何でも一貫したエンディアンで行うことです。通常、さまざまな TCP/IP で使用される「ネットワーク バイト オーダー」が最適です。ライブラリ ルーチンのhtonlとntohlはこれでうまく機能し、通常はかなり適切に最適化されています。
ただし、ネットワーク バイト オーダーが使用されていない場合は、別の方法が必要になる場合があります。整数のサイズとバイト順の 2 つを知っておく必要があります。それがわかれば、抽出するバイト数と、それらを int にまとめる順序がわかります。
sizeof(int) が適切なバイト数であると仮定するコード例:
#include <limits.h>
int bytes_to_int_big_endian(const char *bytes)
{
int i;
int result;
result = 0;
for (i = 0; i < sizeof(int); ++i)
result = (result << CHAR_BIT) + bytes[i];
return result;
}
int bytes_to_int_little_endian(const char *bytes)
{
int i;
int result;
result = 0;
for (i = 0; i < sizeof(int); ++i)
result += bytes[i] << (i * CHAR_BIT);
return result;
}
#ifdef TEST
#include <stdio.h>
int main(void)
{
const int correct = 0x01020304;
const char little[] = "\x04\x03\x02\x01";
const char big[] = "\x01\x02\x03\x04";
printf("correct: %0x\n", correct);
printf("from big-endian: %0x\n", bytes_to_int_big_endian(big));
printf("from-little-endian: %0x\n", bytes_to_int_little_endian(little));
return 0;
}
#endif
どうですか
int int_from_bytes(const char * bytes, _Bool reverse)
{
if(!reverse)
return *(int *)(void *)bytes;
char tmp[sizeof(int)];
for(size_t i = sizeof(tmp); i--; ++bytes)
tmp[i] = *bytes;
return *(int *)(void *)tmp;
}
次のように使用します。
int i = int_from_bytes(bytes, SYSTEM_ENDIANNESS != ARRAY_ENDIANNESS);
にキャストvoid *
するint *
とアラインメントの競合が発生する可能性があるシステムを使用している場合は、次を使用できます
int int_from_bytes(const char * bytes, _Bool reverse)
{
int tmp;
if(reverse)
{
for(size_t i = sizeof(tmp); i--; ++bytes)
((char *)&tmp)[i] = *bytes;
}
else memcpy(&tmp, bytes, sizeof(tmp));
return tmp;
}
ネットワーク ストリームなど、別のマシンで作成されたソースからバイトを読み取る場合を除き、エンディアンを気にする必要はありません。
それを考えると、for ループを使用することはできませんか?
void ReadBytes(char * stream) {
for (int i = 0; i < sizeof(int); i++) {
char foo = stream[i];
}
}
}
それよりも複雑なことを求めていますか?
読み取るデータが 1 バイトより大きい数値で構成されている場合にのみ、エンディアンを気にする必要があります。
sizeof(int) バイトを読み取っていて、それらを int として解釈することを期待している場合、エンディアンが違いを生みます。基本的にエンディアンとは、マシンが一連の 1 バイトを超える値を数値に解釈する方法です。
sizeof(int) チャンクで配列を移動する for ループを使用するだけです。
関数(少なくとも Linux ではntohl
headerにあります) を使用して、ネットワーク オーダー (ネットワーク オーダーはビッグ エンディアンとして定義されます) のバイトをローカル バイト オーダーに変換します。<arpa/inet.h>
そのライブラリ関数は、実行しているプロセッサに関係なく、ネットワークからホストへの正しい変換を実行するために実装されています。
比較できるのに、なぜ読むのですか?
bool AreEqual(int i, char *data)
{
return memcmp(&i, data, sizeof(int)) == 0;
}
すべての整数を何らかの不変形式に変換する必要があるときにエンディアンが心配な場合。htonl と ntohl が良い例です。