[英]How to read numeric from binary data, crossplatform (C/C++)?
我有原始的二進制數據塊(實際上是CBOR
編碼的)。 要讀取數字,我使用常見的形式,例如:
template <typename T> // T can be uint64_t, double, uint32_t, etc...
auto read(const uint8_t *ptr) -> T {
return *((T *)(ptr)); // all endianess-aware functions will be performed later
}
該解決方案可在x86/x86_64
PC和arm/arm64
iOS上運行。 但是,在具有clang
編譯器且默認版本優化級別( -Os
)的arm/armv7
Android上,我收到的SIGBUS
的類型大於1個字節,且代碼為1
(未對齊讀取)。 我用另一個解決方案解決了這個問題:
template <typename T>
auto read(const uint8_t *ptr) -> T {
union {
uint8_t buf[sizeof(T)];
T value;
} u;
memcpy(u.buf, ptr, sizeof(T));
return u.value;
}
是否有任何獨立於平台的解決方案,不會影響性能?
警告 -這個問題的前提是機器的整數表示形式是little-endian。
唯一與平台無關的正確方法是使用memcpy。 您不需要工會。
不用擔心效率。 memcpy是一個魔術函數,編譯器將“做正確的事”。
為x86編譯時的示例:
#include <cstring>
#include <cstdint>
template <typename T>
auto read(const uint8_t *ptr) -> T {
T result;
std::memcpy(&result, ptr, sizeof(T));
return result;
}
extern const uint8_t* get_bytes();
extern void emit(std::uint64_t);
int main()
{
auto x = read<std::uint64_t>(get_bytes());
emit(x);
}
產生匯編程序:
main:
subq $8, %rsp
call get_bytes()
movq (%rax), %rdi ; note - memcpy utterly elided
call emit(unsigned long)
xorl %eax, %eax
addq $8, %rsp
ret
注意:字節序
通過添加運行時字節序檢查,可以使該解決方案真正可移植。 實際上,編譯器將通過檢查來消除該檢查:
constexpr bool is_little_endian()
{
short int number = 0x1;
char *numPtr = (char*)&number;
return (numPtr[0] == 1);
}
template <typename T>
auto read(const uint8_t *ptr) -> T {
T result = 0;
if (is_little_endian())
{
std::memcpy(&result, ptr, sizeof(result));
}
else
{
for (T i = 0 ; i < sizeof(T) ; ++i)
{
result += *ptr++ << 8*i;
}
}
return result;
}
生成的機器代碼不變:
main:
subq $8, %rsp
call get_bytes()
movq (%rax), %rdi
call emit(unsigned long)
xorl %eax, %eax
addq $8, %rsp
ret
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.