在C++中将字节数组转换为整数

6
以下是从字节数组获取int16(short)值的最有效方法吗?
inline __int16* ReadINT16(unsigned char* ByteArray,__int32 Offset){
    return (__int16*)&ByteArray[Offset];
};

如果字节数组中包含与该机器具有相同字节序的字节转储,则将调用此代码。欢迎提供替代方案。

1
int类型通常与机器架构对齐,以便更好地访问内存。如果ByteArray[Offset]没有遵守适当的对齐方式(例如Offset是奇数),那么检索__int16可能不是一个好主意。不过,我们可以等待一些更好的答案。 - iammilind
对于讨论字节对齐的人而言,由于它是一种文件格式,字节数组可以包含单个字节,因此偏移量可能是奇数。这是个问题吗? - user826228
@user826228:是的,如果你想让你的代码具有可移植性,这是一个潜在的问题。如果你知道它只会在x86上运行,并且性能不是关键因素,那么你可以这样做--但我建议不要这样做。 - Paul R
4个回答

9

这取决于您对“高效”一词的理解,但请注意,在某些架构中,如果Offset为奇数,则此方法将失败,因为生成的16位整数将被错误对齐,并且在随后尝试访问它时会导致异常。只有在您可以保证Offset是偶数时才应使用此方法,例如。

inline int16_t ReadINT16(uint8_t *ByteArray, int32_t Offset){
    assert((Offset & 1) == 0); // Offset must be multiple of 2
    return *(int16_t*)&ByteArray[Offset];
};

注意,我稍微更改了一下代码,现在它会直接返回一个16位的数值,因为返回指针再进行解析比直接返回16位数值要低效。我还更换了标准Posix整数类型 - 我建议你也这样做。

1
+1 对齐很重要,没想到有人会注意到。现在大多数架构都支持它,但与对齐访问相比速度较慢。 - Jesus Ramos
如果ByteArray不是从偶数地址开始的呢? - selbie
@selbie:只要ByteArray是静态声明或通过newmalloc分配的,就不应该出现这种情况。但是如果您想绝对确定,那么您可以测试&ByteArray [Offset]的对齐方式,而不仅仅是测试Offset - Paul R
正确的做法是:断言应该是“assert(((ByteArray+offset)&1)==0);”。 - selbie
@selbie:是的,除非你在那里需要一个转换。 - Paul R

4

我很惊讶还没有人提出这个解决方案,它既可以保证对齐安全,又可以在所有架构上正确地运行(只要一个字节有8位)。

inline int16_t ReadINT16(uint8_t *ByteArray, int32_t Offset)
{
    int16_t result;
    memcpy(&result, ByteArray+Offset, sizeof(int16_t));
    return result;
};

我认为可以避免使用memcpy的开销:

inline int16_t ReadINT16(uint8_t *ByteArray, int32_t Offset)
{
    int16_t result;
    uint8_t* ptr1=(uint8_t*)&result;
    uint8_t* ptr2 = ptr1+1;
    *ptr1 = *ByteArray;
    *ptr2 = *(ByteArray+1);
    return result;
};

我相信在x86上对齐问题不会产生异常。如果我没记错的话,当Windows运行在Dec Alpha和其他处理器上时,会捕获对齐异常并进行修复(性能略有下降)。而我确实记得,在SunOS上运行的Sparc处理器遇到对齐问题时会直接崩溃。

我试图避免复制数据的开销。虽然这只是一个小的开销,但我认为使用冗余数据是不好的做法。 - user826228

1
inline __int16* ReadINT16(unsigned char* ByteArray,__int32 Offset)
{     
    return (__int16*)&ByteArray[Offset]; 
}; 

很不幸,这在C++中具有未定义的行为,因为您正在使用两种不同类型访问存储,这在严格别名规则下是不允许的。您可以使用char*访问类型的存储,但反过来不行。

从我之前提出的问题中得出的结论是,唯一安全的方法是使用memcpy将字节复制到int中,然后使用它。(这可能会被优化为您希望的相同代码,所以看起来非常低效)。

您的代码可能会正常工作,大多数人似乎都这样做...但重点是,当某天生成不符合您期望的代码时,您不能向编译器供应商哭泣。


0

我认为这没有问题,这正是我会做的。只要字节数组可以安全访问,并确保偏移量正确(shorts为2个字节,因此您可能希望确保它们不能进行奇数偏移或类似操作)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接