为使用Little或Big endian的计算机编写程序.并有相同的结果



这个问题是关于endian的。

目标是在计算机上的游戏文件中写入2个字节。我想确保使用不同电脑的人,无论使用小恩迪还是大恩迪,都能得到相同的结果。

我应该使用以下哪一个片段?

char a[2] = { 0x5c, 0x7B };
fout.write(a, 2);

int a = 0x7B5C;
fout.write((char*)&a, 2);

非常感谢。

来自维基百科:

在最常见的用法中,endianness表示多字节数中的字节顺序。

因此,对于char a[2] = { 0x5c, 0x7B };a[1]将始终为0x7B

但是,对于int a = 0x7B5C;,char*oneByte=(char*(/a;(char*(oneByte[0];可能是0x7B或0x5C,但正如您所看到的,您必须使用强制类型转换和字节指针(请记住char[1]时的未定义行为,这仅用于解释目的(。

经常使用的一种方法是将"签名"或"幻数"作为文件中的第一个数据-通常是一个16位整数,其值在读回时取决于读取平台是否具有与写入平台相同的endianness。如果检测到不匹配,则从文件中读取的所有数据(超过一个字节(都需要进行字节交换。

这里有一些大纲代码:

void ByteSwap(void *buffer, size_t length)
{
unsigned char *p = static_cast<unsigned char *>(buffer);
for (size_t i = 0; i < length / 2; ++i) {
unsigned char tmp = *(p + i);
*(p + i) = *(p + length - i - 1);
*(p + length - i - 1) = tmp;
}
return;
}
bool WriteData(void *data, size_t size, size_t num, FILE *file)
{
uint16_t magic = 0xAB12; // Something that can be tested for byte-reversal
if (fwrite(&magic, sizeof(uint16_t), 1, file) != 1) return false;
if (fwrite(data, size, num, file) != num) return false;
return true;
}
bool ReadData(void *data, size_t size, size_t num, FILE *file)
{
uint16_t test_magic;
bool is_reversed;
if (fread(&test_magic, sizeof(uint16_t), 1, file) != 1) return false;
if (test_magic == 0xAB12) is_reversed = false;
else if (test_magic == 0x12AB) is_reversed = true;
else return false; // Error - needs handling!
if (fread(data, size, num, file) != num) return false;
if (is_reversed && (size > 1)) {
for (size_t i = 0; i < num; ++i) ByteSwap(static_cast<char *>(data) + (i*size), size);
}
return true;
}

当然,在现实世界中,您不需要在每个输入/输出操作中写入/读取的"magic"数字-每个文件只需一次,并存储is_reversed标志以备将来读取数据时使用。

此外,如果正确使用C++代码,您可能会使用std::stream参数,而不是我展示的FILE*——但我发布的示例是从我在项目中实际使用的代码中提取的(只做了很少的修改((仅用于进行此测试(。但转换为更好地使用现代C++应该很简单。

请随时要求进一步澄清和/或解释。

注意:我提供的ByteSwap功能并不理想!几乎可以肯定的是,它违反了严格的别名规则,如果不小心使用,很可能会在某些平台上导致未定义的行为。此外,对于小数据单元(如int变量(,它不是最有效的方法。可以(也应该(提供自己的字节反转函数来处理特定类型的变量——用不同的参数类型重载函数是一个很好的例子(

我应该使用以下哪一个片段?

第一个。无论本机端序如何,它都具有相同的输出。

但您会发现,如果您需要将这些字节解释为某个整数值,那就不那么简单了。CCD_ 11可以表示0x5c7B(大端序(或0x7B5c(小端序(。那么,你打算选哪一个呢?

整数的跨平台解释的解决方案是决定读取和写入的特定字节顺序。事实上,跨平台数据的"标准"是使用big-endian。

要用big-endian写一个数字,首先将输入值右移,使最高有效字节位于最低有效字节的位置。屏蔽所有其他字节(在第一次迭代中技术上是多余的,但我们很快就会循环回来(。将此字节写入输出。按重要性顺序对所有其他字节重复此操作。

无论本机端序如何,该算法都会产生相同的输出——如果您遇到特殊的"中间"端序系统,它甚至可以在其中工作。写入little-endian类似,但顺序相反。

要读取大端序值,请读取输入的第一个字节,将其左移,使其到达最高有效字节的位置。使用逐位或将移位的字节与结果(最初为零(组合。将下一个字节移到第二个最高有效位,依此类推

了解计算机的终结性?

要了解系统的字节序,可以在即将推出的C++20中使用std::endian。在此之前,您可以使用endian.h标头中特定于实现的宏。或者你可以像你建议的那样做一个简单的计算。

但是,您永远不需要知道系统的端序性。你可以简单地使用我所描述的算法,这些算法在所有端序的系统上工作,而不必知道端序是什么

最新更新