使用 C++ 读取文件时切换字节序的最快方法是什么?

What's the fastest way to switch endianness when reading from a file with C++?

有人向我提供了一个二进制文件供我读取,其中包含一系列原始值。为了简单起见,假设它们是无符号整数值,4 字节或 8 字节长。对我来说不幸的是,这些值的字节顺序与我的处理器的字节顺序不兼容(小与大或反之亦然;不要介意奇怪的 PDF 字节顺序等);我希望内存中的这些数据具有正确的字节顺序。

考虑到我正在从文件中读取数据,最快的方法是什么?如果不值得利用这个事实,请解释原因。

考虑到您正在从文件中读取数据这一事实,与文件 IO 相比,切换字节序的方式对运行时的影响微不足道。

可能会产生重大影响的是您读取数据的方式。尝试乱序读取字节不是一个好主意。只需按顺序读取字节,然后切换字节序。这将读取和字节交换分开。

通常希望从字节交换代码中得到什么,当然在读取文件的情况下,它适用于任何字节顺序并且不依赖于特定的体系结构说明。

char* buf = read(); // let buf be a pointer to the read buffer
uint32_t v;

// little to native
v = 0;
for(unsigned i = 0; i < sizeof v; i++)
    v |= buf[i] << CHAR_BIT * i;

// big to native
v = 0;
for(unsigned i = 0; i < sizeof v; i++)
    v |= buf[i] << CHAR_BIT * (sizeof v - i);

无论本机是大、小还是中间字节序类型之一,这都适用。

当然,boost has already implemented these for you, so there is no need to re-implement. Also, there are the ntoh? 函数族由 POSIX 和 windows C 库提供,可用于转换大端 to/from 本机。

不是最快的,但是一种可移植的方法是将文件读入一个(无符号的)int 数组,将 int 数组别名为 char one(根据严格的别名规则允许)并交换内存中的字节。

完全便携的方式:

swapints(unsigned int *arr, size_t l) {
    unsigned int cur;
    char *ix;
    for (size_t i=0; i<l; i++) {
        int cur;
        char *dest = static_cast<char *>(&cur) + sizeof(int);
        char *src = static_cast<char *>(&(arr[i]));
        for(int j=0; j<sizeof(int); j++) *(--dest) = *(src++);
        arr[i] = cur;
    }
}

但是如果您不需要便携性,有些系统会提供交换功能。例如 BSD 系统有 bswap16bswap32bswap64 分别在 uint16_tuint32_tuint_64_t 中交换字节。毫无疑问,Microsoft 或 GNU-Linux 世界中存在等效功能。

或者,如果您知道该文件是 网络 顺序(big endian)而您的处理器不是,您可以使用 ntohsntohl 函数分别用于 uint16_tuint32_t.

备注(根据 AndrewHenle 的评论):无论主机字节序如何,始终可以使用 ntohsntohl - 只是它们在大端系统上是无操作的