复制整数的低位字节,同时保留字节序
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
的每个字节来实现该功能,但我的所有尝试都失败了一个或另一个字节序。
是否可以以独立于平台的方式执行此操作,或者我是否需要使用 #ifdef
s 并为每个字节顺序编写一段单独的代码?
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
请注意,平台依赖的原因是什么(如果是字节顺序或其他)并没有真正的区别,一旦你有了平台依赖,你就不能拥有平台独立性。
我需要编写一个函数,将给定整数的指定数量的低位字节复制到内存中的地址,同时保留它们的顺序。
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
的每个字节来实现该功能,但我的所有尝试都失败了一个或另一个字节序。
是否可以以独立于平台的方式执行此操作,或者我是否需要使用 #ifdef
s 并为每个字节顺序编写一段单独的代码?
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 ofval
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
请注意,平台依赖的原因是什么(如果是字节顺序或其他)并没有真正的区别,一旦你有了平台依赖,你就不能拥有平台独立性。