Используйте uint32_t для хранения четырех отдельных значений uint8_t - PullRequest
3 голосов
/ 07 марта 2019

Я хочу использовать 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;

Ответы [ 2 ]

3 голосов
/ 07 марта 2019

Ваша первоначальная ревизия имела правильный хотя и окольный подход для варианта 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-битные диапазоны, вы ИЛИ их.

Правильный подход будет

// 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-битного диапазона на значение uint8_t в RHS.

1 голос
/ 07 марта 2019

Универсальная функция (для 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
...