最佳字节序转换和有符号整数

时间:2012-06-16 14:03:05

标签: c type-conversion endianness

我正在寻找读取存储在二进制文件中的数值的最快方法。

我已经完成了一些似乎有用的功能,但我想得到关于我的实现是否良好的反馈。

以下是我从4字节小端块获取有符号整数的方法:

signed long int from4li(char const * const buffer)
{
    signed long int value = 0;

    value += (unsigned char) buffer[3];
    value <<= 8;
    value += (unsigned char) buffer[2];
    value <<= 8;
    value += (unsigned char) buffer[1];
    value <<= 8;
    value += (unsigned char) buffer[0];

    return value;
}

这也适用于无符号整数,但我最初为无符号整数做了一个不同的实现(失败的有符号整数,我不知道为什么):

unsigned long int fromu4li(char const * const buffer)
{
    unsigned long int value = 0;

    value += (unsigned char) buffer[0] << 8 * 0;
    value += (unsigned char) buffer[1] << 8 * 1;
    value += (unsigned char) buffer[2] << 8 * 2;
    value += (unsigned char) buffer[3] << 8 * 3;

    return value;
}

我更确定从整数到小端字符串缓冲区的转换,我认为可能无法进一步优化:

void to4li(long int const value, char * const buffer)
{
    buffer[0] = value >> 8 * 0;
    buffer[1] = value >> 8 * 1;
    buffer[2] = value >> 8 * 2;
    buffer[3] = value >> 8 * 3;
}

我也认为可以更快地使用memcpy,但是要使用memcpy我必须知道主机系统的字节顺序。

我真的不想依赖主机系统的字节序,因为我认为我的代码应该独立于主机系统的内部数据表示。

那么,这是进行转换的正确方法,还是可以改进我的功能?

2 个答案:

答案 0 :(得分:0)

使用按位OR似乎是一个好主意,但有一些奇怪的事情:

经过测试,似乎c0c1c2c3需要是无符号字符才能使此解决方案正常工作。再说一次,我不知道为什么:

例如,取为0x8080,即-3264(带符号)或32896(无符号)。

使用

char c0 = 0x80;
char c1 = 0x80;

我明白了:

uint16_t res = (c0 << 0) | (c1 << 8);
// res = 65408 ???

但是

uint16_t res = ((unsigned char) c0 << 0) | ((unsigned char) c1 << 8);
// res = 32896 ok

答案 1 :(得分:0)

一种更简单的方法,也就是避免由于对有符号整数变量进行位移而导致未定义的行为,只是按位复制数据:

int32_t get(char const * const buf)
{
    int32_t result;
    char * const p = reinterpret_cast<char *>(&result);
    std::copy(buf, buf + sizeof result, p);
    return result;
}

此代码假定数据与machien具有相同的字节序。或者,您可以使用std::copy_backward 反转字节序。

此方法取决于流数据和主机端字节顺序,因此它不如无符号整数的代数解决方案那么优雅,依赖于流数据。但是,由于签名整数无论如何都依赖于平台,因此这可能是一种可接受的折衷方案。

(仅供比较,对于无符号整数,我更喜欢这个与机器无关的代码:

template <typename UInt>
typename std::enable_if<std::is_unsigned<UInt>::value, UInt>::type
get_from_le(unsigned char * const buf)
{
    UInt result;
    for (std::size_t i = 0; i != sizeof(UInt); ++i)
        result += (buf[i] << (8 * i));
    return result;
}

用法:auto ui = get_from_le<uint64_t>(buf);

对于big-endian版本,请将[i]替换为[sizeof(UInt) - i - 1]。)