Android 如何从二进制数据中读取数字,跨平台(C/C++)?

Android 如何从二进制数据中读取数字,跨平台(C/C++)?,android,c++,ios,cross-platform,Android,C++,Ios,Cross Platform,我有原始二进制数据块,实际上是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 } 是否有任何平台无关

我有原始二进制数据块,实际上是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
}
是否有任何平台无关的解决方案不会影响性能?

警告-此答案假设机器的整数表示为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);

}
注:余弦

通过添加运行时端点检查,可以使此解决方案真正可移植。实际上,检查将被省略,因为编译器将看穿它:

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;
}
警告-这个答案假设机器的整数表示是小端点,问题也是如此

唯一独立于平台且正确的方法是使用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);

}
注:余弦

通过添加运行时端点检查,可以使此解决方案真正可移植。实际上,检查将被省略,因为编译器将看穿它:

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;
}

我认为这可能是你将得到的最好结果。使用适当的反序列化,而不是这些未定义的行为重新解释。您已经遇到了一些问题,可能还会有更多问题。我认为这可能是您将得到的最好结果。请使用适当的反序列化,而不是这些未定义的行为重新解释。你已经遇到一些问题了,可能还会有更多。胡说!唯一兼容且独立于平台的方法是使用位移位实现SEERIALIzation!memcpy不关心endianess或表示。endianess不是问题,因为我需要的所有编译器都有转换字节顺序的内在机制。当没有内部变量时,我会回退到位移位。@olaf我知道endian的ness问题,并假设OP知道它。我正在解决将字节流转换为整数的问题。我将在答案中添加一条警告。@Olaf已更新,并添加了一个endian验证解决方案,其优点是在一个小小的endian x86上仍然100%有效。@SBKarr:使用正确的序列化首先有什么问题?一个好的编译器不会产生比重新解释的黑客方法更糟糕的代码,但是不需要测试,也不需要其他依赖于目标的东西。胡说八道!唯一兼容且独立于平台的方法是使用位移位实现SEERIALIzation!memcpy不关心endianess或表示。endianess不是问题,因为我需要的所有编译器都有转换字节顺序的内在机制。当没有内部变量时,我会回退到位移位。@olaf我知道endian的ness问题,并假设OP知道它。我正在解决将字节流转换为整数的问题。我将在答案中添加一条警告。@Olaf已更新,并添加了一个endian验证解决方案,其优点是在一个小小的endian x86上仍然100%有效。@SBKarr:使用正确的序列化首先有什么问题?一个好的编译器不会生成比重新解释的黑客方法更糟糕的代码,但是不需要测试,也不需要其他依赖于目标的东西。
main:
        subq    $8, %rsp
        call    get_bytes()
        movq    (%rax), %rdi
        call    emit(unsigned long)
        xorl    %eax, %eax
        addq    $8, %rsp
        ret