复制整数的低位字节,同时保留字节序

Copy low-order bytes of an integer whilst preserving endianness

我需要编写一个函数,将给定整数的指定数量的低位字节复制到内存中的地址,同时保留它们的顺序。

void lo_bytes(uint8_t *dest, uint8_t no_bytes, uint32_t val)

我希望用法如下所示:

uint8 dest[3];
lo_bytes(dest, 3, 0x44332211);
// Big-endian:    dest = 33 22 11
// Little-endian: dest = 11 22 33

我尝试使用移位 memcpy 并使用 for 循环遍历 val 的每个字节来实现该功能,但我的所有尝试都失败了一个或另一个字节序。

是否可以以独立于平台的方式执行此操作,或者我是否需要使用 #ifdefs 并为每个字节顺序编写一段单独的代码?

int detect_endianess(void)  //1 if little endian 0 if big endianes
{
    union
    {
        uint16_t u16;
        uint8_t u8[2];
    }val = {.u16 = 0x1122};

    return val.u8[0] == 0x22;
}

void lo_bytes(void *dest, uint8_t no_bytes, uint32_t val)
{
    if(detect_endianess())
    {
        memcpy(dest, &val, no_bytes);
    }
    else
    {
        memcpy(dest, (uint8_t *)(&val) + sizeof(val) - no_bytes, no_bytes);
    }
}

I've tried to implement the function using bit-shifts, memcpy, and iterating over each byte of val with a for-loop, but all of my attempts failed to work on either one or the other endianness.

所有算术,包括按位算术,都是根据操作数的定义的,而不是它们的表示。这对您来说还不够,因为您希望根据类型 uint32_t.

的表示样式的详细信息获得不同的结果

您可以通过各种方法对对象表示进行操作,但您仍然需要知道要对哪些字节进行操作。这需要某种形式的检测。如果 big-endian 和 little-endian 是您唯一关心支持的字节顺序,那么我赞成类似于 @P__J__ 的回答中给出的方法:

void lo_bytes(uint8_t *dest, uint8_t no_bytes, uint32_t val) {
    static const union { uint32_t i; uint8_t a[4] } ubytes = { 1 };

    memcpy(dest, &val + (1 - ubytes.a[0]) * (4 - no_bytes), no_bytes);
}

如果 uint32_t 的表示是大端,则表达式 (1 - ubytes.a[0]) 的计算结果为 1,在这种情况下,高位字节出现在 val 的表示的开头。在那种情况下,我们要跳过表示的第一个 4 - no_bytes 并复制其余部分。另一方面,如果 uint32_t 具有小端表示法,则 (1 - ubytes.a[0]) 的计算结果为 0,结果 memcpy 从表示法的开头开始。在每种情况下,无论从 val 的表示中复制哪个字节,它们的顺序都会保持不变。这就是 memcpy() 所做的。

Is it possible to do this in a platform-independent way, or do I need to use #ifdefs and have a separate piece of code for each endianness?

不,这甚至没有意义。任何关心平台特定特征(例如字节序)的东西都不能与平台无关。

示例 1(平台无关):

// Copy the 3 least significant bytes to dest[]

dest[0] = value & 0xFF; dest[1] = (value >> 8) & 0xFF; dest[2] = (value >> 16) & 0xFF;

示例 2(独立于平台):

// Copy the 3 most significant bytes to dest[]

dest[0] = (value >> 8) & 0xFF; dest[1] = (value >> 16) & 0xFF; dest[2] = (value >> 24) & 0xFF;

示例 3(取决于平台):

// I want the least significant bytes on some platforms and the most significant bytes on other platforms

#ifdef PLATFORM_TYPE_A
    dest[0] = value & 0xFF; dest[1] = (value >> 8) & 0xFF; dest[2] = (value >> 16) & 0xFF;
#endif
#ifdef PLATFORM_TYPE_B
    dest[0] = (value >> 8) & 0xFF; dest[1] = (value >> 16) & 0xFF; dest[2] = (value >> 24) & 0xFF;
#endif

请注意,平台依赖的原因是什么(如果是字节顺序或其他)并没有真正的区别,一旦你有了平台依赖,你就不能拥有平台独立性。