使用uint32_t存储四个单独的uint8_t值

问题描述 投票:3回答:2

我想使用uint32_t存储4个独立的uint8_t值,并且能够单独读取/写入每个值。

是否可以安全地执行以下任一操作来设置每个8位范围的值,如果是,哪个更好(更快,更便携)?我不一定试图一次性设置它们,我只是说明如何在任何时间点设置每个8位值。

uint32_t x = ...;

选项1:

((uint8_t *)(&x))[0] = a;
((uint8_t *)(&x))[1] = b;
((uint8_t *)(&x))[2] = c;
((uint8_t *)(&x))[3] = d;

选项2:

x = (x & 0xFFFFFF00) | (uint32_t) a;
x = (x & 0xFFFF00FF) | (uint32_t) b << 8;
x = (x & 0xFF00FFFF) | (uint32_t) c << 16;
x = (x & 0x00FFFFFF) | (uint32_t) d << 24;
c bit-manipulation unsigned-integer
2个回答
3
投票

您的初始修订版本是正确的 尽管是回旋处 选项2的方法,即

// a, b, c, and d are of initialized and of type uint8_t
uint32_t x = ...;
x = (x & 0xFFFFFF00) | (uint32_t) a;
x = (x & 0xFFFF00FF) | (uint32_t) b << 8;
x = (x & 0xFF00FFFF) | (uint32_t) c << 16;
x = (x & 0x00FFFFFF) | (uint32_t) d << 24;

选项2的此修订版错误:

uint32_t x = ...;
x |= (uint32_t) a;
x |= (uint32_t) b << 8;
x |= (uint32_t) c << 16;
x |= (uint32_t) d << 24;

即使x被初始化,它仍然是错误的,因为你没有设置8位范围,你正在对它们进行ORing。

正确的方法是

// a, b, c, and d are of initialized and of type uint8_t
uint32_t x = (uint32_t) a;
x |= (uint32_t) b << 8;
x |= (uint32_t) c << 16;
x |= (uint32_t) d << 24;

或者更简洁

// a, b, c, and d are of initialized and of type uint8_t
uint32_t x =
    (uint32_t) a
  | (uint32_t) b << 8
  | (uint32_t) c << 16
  | (uint32_t) d << 24;

选项1的问题在于它假定uint32_t的字节序首先是LSB,因此不是便携式解决方案。


在收到有关您提出的问题的说明后,您的初始修订版(本答复中的第一个代码块)是正确的方法。它将剩余的24位保持不变,同时将特定的8位范围设置为RHS上的uint8_t值。


1
投票

通用功能(适用于pos8 16和24):

uint32_t setb(uint32_t val, uint8_t a, int pos)
{
    val &= ~(0xffUL << pos);
    val |= (uint32_t)a << pos;
    return val;
}

如果你不喜欢轮班:

uint32_t f(uint8_t a, uint8_t b,uint8_t c,uint8_t d) {    
      return a + 0x100UL * b + 0x10000UL * c + 0x1000000UL * d;
 }

任何好的优化编译器都会生成非常高效的代码

gcc ARM

f:
        add     r0, r0, r2, lsl #16
        add     r0, r0, r3, lsl #24
        add     r0, r0, r1, lsl #8
        bx      lr

clang x86

f:                                      # @f
        shl     esi, 8
        lea     eax, [rsi + rdi]
        shl     edx, 16
        or      eax, edx
        shl     ecx, 24
        or      eax, ecx
        ret

只有在非常小的微观上,我才会推荐工会方式

uint32_t g(uint8_t a, uint8_t b,uint8_t c,uint8_t d) 
{    
    union
    {
        uint32_t v32;
        uint8_t v8[4];
    }x = {.v8[0] = a, .v8[1] = b, .v8[2] = c, .v8[3] = d};
    return x.v32;
}

因为它更容易优化:

__zero_reg__ = 1
f:
        push r16
        push r17
        ldi r25,lo8(0)
        ldi r26,lo8(0)
        ldi r27,hi8(0)
        add r24,r22
        adc r25,__zero_reg__
        adc r26,__zero_reg__
        adc r27,__zero_reg__
        ldi r21,lo8(0)
        subi r20,lo8(-(8))
        sbci r21,hi8(-(8))
        rjmp 2f
1:      lsl r24
        rol r25
        rol r26
        rol r27
2:      dec r20
        brpl 1b
        ldi r19,lo8(0)
        subi r18,lo8(-(16))
        sbci r19,hi8(-(16))
        rjmp 2f
1:      lsl r24
        rol r25
        rol r26
        rol r27
2:      dec r18
        brpl 1b
        mov r19,r24
        clr r18
        clr r17
        clr r16
        mov r22,r16
        mov r23,r17
        mov r24,r18
        mov r25,r19
        pop r17
        pop r16
        ret
g:
        push r16
        push r17
        mov r25,r18
        mov r17,r22
        mov r22,r24
        mov r23,r17
        mov r24,r20
        pop r17
        pop r16
        ret
© www.soinside.com 2019 - 2024. All rights reserved.