从较小到较大的整数指针使用 memcpy() 的 Endian-independent 方式

问题描述

假设我有两个数组。

uint8_t[SIZE] src = { 0 };
uint32_t[SIZE] dst = { 0 };

uint8_t* srcPtr;  // Points to current src value
uint32_t* dstPtr; // Points to current dst value

src 保存有时需要放入 dst 的值。重要的是,来自 src 的值可能是 8 位、16 位或 32 位,并且不一定正确对齐。因此,假设我希望使用如下所示的 memcpy() 来复制 16 位值

memcpy(dstPtr,srcPtr,2);

我会在这里遇到字节序问题吗?这在小端系统上工作正常,因为如果我想复制 8,那么 srcPtr 有 08 然后 00 dstPtr 的字节将是 08 00 00 00 并且值将是 8,如预期。

但是如果我在一个大端系统上,srcPtr 将是 00 然后是 08,并且 dstPtr 的字节将是 00 08 00 00(我认为),这将采取值为 524288。

编写此副本的字节序独立方式是什么?

解决方法

我会在这里遇到字节序问题吗?

是的。您不是在复制,而是从一种格式转换为另一种格式(将几个无符号整数打包成一个更大的无符号整数)。

编写此副本的字节序独立方式是什么?

简单的方法是使转换显式,例如:

    for(int i = 0; i < something; i++) {
        dest[i] = (uint32_t)src[i*4] | ((uint32_t)src[i*4+1] <<  8) |
                    ((uint32_t)src[i*4+2] <<  16) | ((uint32_t)src[i*4+3] <<  24);
    }

但是,对于使用 memcpy() 的情况,它可能会更快,并且编译后不会改变;所以你可以做这样的事情:

#ifdef BIG_ENDIAN
    for(int i = 0; i < something; i++) {
        dest[i] = (uint32_t)src[i*4] | ((uint32_t)src[i*4+1] <<  8) |
                    ((uint32_t)src[i*4+2] <<  16) | ((uint32_t)src[i*4+3] <<  24);
    }
#else
    memcpy(dest,src,something*4);
#endif

注意:您还必须在适当的时候定义 BIG_ENDIAN 宏 - 例如当您知道目标架构需要它时,可能在启动编译器时使用 -D BIG_ENDIAN 命令行参数。


我在 src 中存储不是 16 位对齐的 16 位值,然后需要将其放入 64 位整数

这又增加了一个问题——某些架构不允许未对齐的访问。您也需要使用显式转换(读取 2 个单独的 uint8_t,而不是未对齐的 uint16_t)来避免此问题。

,

我会在这里遇到字节序问题吗?

不一定是字节序问题本身,但是是的,您描述的特定方法会遇到整数表示的问题。

这适用于 小端系统,因为如果我想复制 8,那么 srcPtr 有 08 那么 00 dstPtr 处的字节将是 08 00 00 00 并且该值将是 8,正如预期的那样。

您似乎在那里做出假设

  • 目标的更多字节将被修改,而不是您实际复制,或者
  • 目的地的相关部分已预先设置为全零字节。

但您需要了解 memcpy() 将准确复制请求的字节数。不会从指定的源读取更多的内容,并且不会在目标中修改超过的内容。尤其是源指针和目的指针指向的对象的数据类型对memcpy()的操作没有影响。

编写此副本的字节序独立方式是什么?

最自然的方法是通过简单的赋值,依靠编译器来执行必要的转换:

*dstPtr = *srcPtr;

但是,我认为您强调数组可能未对齐的前景,因为您担心取消引用源和/或目标指针可能不安全。实际上,对于指向 char 的指针来说不是这种情况,但对于指向其他类型的指针来说可能是这种情况。对于将 memcpy 作为从数组中读取的唯一安全方法的情况,转换值表示的最便携方法仍然依赖于实现。例如:

uint8_t* srcPtr = /* ... */;
uint32_t* dstPtr = /* ... */;

uint16_t srcVal;
uint32_t dstVal;

memcpy(&srcVal,srcPtr,sizeof(srcVal));
dstVal = srcVal;  // conversion is automatically performed
memcpy(dstPtr,&dstVal,sizeof(dstVal));