Question

I wrote this test to convert a uint8[4] into a uint32_t, but how would I convert the output of u32 back to the original uint8[4]? I assume it can be done the same way via bit shifting, but i'm not sure how...

uint32_t u32(uint8_t b[4]){
  uint32_t u;
  u = b[0];
  u = (u  << 8) + b[1];
  u = (u  << 8) + b[2];
  u = (u  << 8) + b[3];
  return u;
}

void p(uint32_t value){
        printf("\nuint32: %u\n",value);
}

int main(){
 uint8_t b[4];
 char a[4] = "test";
 char tmp[32];

 int i;
 for(i=0;i<4;i++){
   b[i] = a[i];
   sprintf(tmp,"%s%u",tmp,b[i]);
 }

 printf("uint8: ");

 for(i=0;i<4;i++)
   printf("%u",b[i]);

uint32_t t2 = u32(b);
 p(t2);
 return 0;
}
Was it helpful?

Solution

I tried to do it a little bit different and using known functions and not self implemented. My main point is to teach you that bytes lay in the memory the interpretation you give them depends on context. Note there are some endianness issues one should take care. Moreover the best lesson is to use bit-wise operations.

#include <stdint.h>
#include <stdio.h>
#include <arpa/inet.h>
int main()
{
        uint8_t a[4]={0x1,0x2,0x3,0x4};
        uint32_t b = *((uint32_t*) a);
        /* turning an array to unit32 */
        printf("0x%x\n",htonl(b));
        /* turn an uint32 to array */
        uint32_t c = htonl(b);
        uint8_t d[4] = {0};
        printf("0x%x\n",c);
        for (int i=0; i<4 ;++i)
                d[i] = ((uint8_t*)&c)[3-i];
        for (int i=0; i<4 ;++i)
                printf("0x%x\n",d[i]);
        return 0;
}

The output is:

0x1020304
0x1020304
0x1
0x2
0x3
0x4

Disassembling it [you can load the program using lldb or gdb]:

root# otool -tv a.out
a.out:
(__TEXT,__text) section
_main:
0000000100000e50    pushq   %rbp
0000000100000e51    movq    %rsp, %rbp
0000000100000e54    subq    $0x30, %rsp
0000000100000e58    movl    $0x0, 0xfffffffffffffffc(%rbp)
0000000100000e5f    movl    0x12b(%rip), %eax
0000000100000e65    movl    %eax, 0xfffffffffffffff8(%rbp)
0000000100000e68    movl    0xfffffffffffffff8(%rbp), %eax
0000000100000e6b    movl    %eax, 0xfffffffffffffff4(%rbp)
0000000100000e6e    movl    0xfffffffffffffff4(%rbp), %edi
0000000100000e71    callq   0x100000f50
0000000100000e76    leaq    0x117(%rip), %rdi
0000000100000e7d    movl    %eax, %esi
0000000100000e7f    movb    $0x0, %al
0000000100000e81    callq   0x100000f66
0000000100000e86    movl    0xfffffffffffffff4(%rbp), %edi
0000000100000e89    movl    %eax, 0xffffffffffffffe0(%rbp)
0000000100000e8c    callq   0x100000f50
0000000100000e91    movl    $0x0, %esi
0000000100000e96    movabsq $0x4, %rdx
0000000100000ea0    leaq    0xffffffffffffffec(%rbp), %rcx
0000000100000ea4    movl    %eax, 0xfffffffffffffff0(%rbp)
0000000100000ea7    movq    %rcx, %rdi
0000000100000eaa    callq   0x100000f60
0000000100000eaf    leaq    0xde(%rip), %rdi
0000000100000eb6    movl    0xfffffffffffffff0(%rbp), %esi
0000000100000eb9    movb    $0x0, %al
0000000100000ebb    callq   0x100000f66
0000000100000ec0    movl    $0x0, 0xffffffffffffffe8(%rbp)
0000000100000ec7    movl    %eax, 0xffffffffffffffdc(%rbp)
0000000100000eca    cmpl    $0x4, 0xffffffffffffffe8(%rbp)
0000000100000ed1    jge 0x100000efe
0000000100000ed7    movl    $0x3, %eax
0000000100000edc    subl    0xffffffffffffffe8(%rbp), %eax
0000000100000edf    movslq  %eax, %rcx
0000000100000ee2    movb    0xfffffffffffffff0(%rbp,%rcx), %dl
0000000100000ee6    movslq  0xffffffffffffffe8(%rbp), %rcx
0000000100000eea    movb    %dl, 0xffffffffffffffec(%rbp,%rcx)
0000000100000eee    movl    0xffffffffffffffe8(%rbp), %eax
0000000100000ef1    addl    $0x1, %eax
0000000100000ef6    movl    %eax, 0xffffffffffffffe8(%rbp)
0000000100000ef9    jmpq    0x100000eca
0000000100000efe    movl    $0x0, 0xffffffffffffffe4(%rbp)
0000000100000f05    cmpl    $0x4, 0xffffffffffffffe4(%rbp)
0000000100000f0c    jge 0x100000f3c
0000000100000f12    leaq    0x7b(%rip), %rdi
0000000100000f19    movslq  0xffffffffffffffe4(%rbp), %rax
0000000100000f1d    movzbl  0xffffffffffffffec(%rbp,%rax), %esi
0000000100000f22    movb    $0x0, %al
0000000100000f24    callq   0x100000f66
0000000100000f29    movl    %eax, 0xffffffffffffffd8(%rbp)
0000000100000f2c    movl    0xffffffffffffffe4(%rbp), %eax
0000000100000f2f    addl    $0x1, %eax
0000000100000f34    movl    %eax, 0xffffffffffffffe4(%rbp)
0000000100000f37    jmpq    0x100000f05
0000000100000f3c    movl    $0x0, %eax
0000000100000f41    addq    $0x30, %rsp
0000000100000f45    popq    %rbp
0000000100000f46    ret
0000000100000f47    nopw    (%rax,%rax)
__OSSwapInt32:
0000000100000f50    pushq   %rbp
0000000100000f51    movq    %rsp, %rbp
0000000100000f54    movl    %edi, 0xfffffffffffffffc(%rbp)
0000000100000f57    movl    0xfffffffffffffffc(%rbp), %edi
0000000100000f5a    bswapl  %edi
0000000100000f5c    movl    %edi, %eax
0000000100000f5e    popq    %rbp
0000000100000f5f    ret

OTHER TIPS

Your u32() function is a good exercise in host-endian-agnostic code reading a big-endian number.
It can certainly be reversed easily, just start from the end and go to the start of your function, disassembling your uint32_t:

void u8from32 (uint8_t b[4], uint32_t u32)
    b[3] = (uint8_t)u32;
    b[2] = (uint8_t)(u32>>=8);
    b[1] = (uint8_t)(u32>>=8);
    b[0] = (uint8_t)(u32>>=8);
}

Another possible solution with least computational resource requirement, no dreaded casting and very portable.

uint32_t v = 0x11223344;

union ui32_to_ui8 {
    uint32_t ui32;
    uint8_t ui8[4];
};

ui32_to_ui8 u;

// ----- uint32_t => uint8_t array ------------------------
u.ui32 = v;

// you can read off like this
for (unsigned idx = 0; idx < 3; ++idx) {
    b[idx] = u.ui8[idx];  // tx each byte
}

// OR you can do this
b[0] = u.ui8[0];
b[1] = u.ui8[1];
b[2] = u.ui8[2];
b[3] = u.ui8[3];

// ----- uint8_t array => uint32_t ------------------------
for (unsigned idx = 0; idx < 3; ++idx) {
    u.ui8[idx] = b[idx];
}

v = u.ui32;  // read out the 32 bit value.

Use bit masks.

For example:

0xabcd & 0xff00 = 0xab00
0xabcd & 0x00ff = 0x00cd

In respects to 32 bit terms, the same idea would apply. Just mask over the areas you desire, and then bit shift rightwards as necessary.

To convert from uint8_t and uint32_t and back, I found these functions useful:

uint32_t u8_to_u32(const uint8_t* bytes) {
  // Every uint32_t consists of 4 bytes, therefore we can shift each uint8_t
  // to an appropriate location.
  // u32   ff  ff  ff  ff
  // u8[]  0   1   2   3
  uint32_t u32 = (bytes[0] << 24) + (bytes[1] << 16) + (bytes[2] << 8) + bytes[3];
  return u32;
}

// We pass an output array in the arguments because we can not return arrays
uint8_t* u32_to_u8(const uint32_t u32, uint8_t* u8) {
  // To extract each byte, we can mask them using bitwise AND (&)
  // then shift them right to the first byte.
  u8[0] = (u32 & 0xff000000) >> 24;
  u8[1] = (u32 & 0x00ff0000) >> 16;
  u8[2] = (u32 & 0x0000ff00) >> 8;
  u8[3] = u32 & 0x000000ff;
}

Here's a program to test these functions:

#include <stdio.h>
#include <stdint.h>

uint32_t u8_to_u32(const uint8_t* bytes) {
  uint32_t u32 = (bytes[0] << 24) + (bytes[1] << 16) + (bytes[2] << 8) + bytes[3];
  return u32;
}

void u32_to_u8(const uint32_t u32, uint8_t* u8) {
  u8[0] = (u32 & 0xff000000) >> 24;
  u8[1] = (u32 & 0x00ff0000) >> 16;
  u8[2] = (u32 & 0x0000ff00) >> 8;
  u8[3] = u32 & 0x000000ff;
}

int main() {
  uint8_t  test_1_u8[4] = {0x12, 0x34, 0x56, 0x78};
  uint32_t test_1_u32   = u8_to_u32(test_1_u8);
  printf("test_1_u32 == 0x12345678 -> %d\n", test_1_u32 == 0x12345678);
  
  uint32_t test_2_u32   = 0x87654321;
  uint8_t  test_2_u8[4];
  u32_to_u8(test_2_u32, test_2_u8);
  printf("0x87654321 can be rewritten as\n0x87, 0x65, 0x43, 0x21\n");
  printf("0x87 == test_2_u8[0] -> %d\n", 0x87 == test_2_u8[0]);
  printf("0x65 == test_2_u8[1] -> %d\n", 0x65 == test_2_u8[1]);
  printf("0x43 == test_2_u8[2] -> %d\n", 0x43 == test_2_u8[2]);
  printf("0x21 == test_2_u8[3] -> %d\n", 0x21 == test_2_u8[3]);
  
  return 0;
}

The output that I get is:

test_1_u32 == 0x12345678 -> 1
0x87654321 can be rewritten as
0x87, 0x65, 0x43, 0x21
0x87 == test_2_u8[0] -> 1
0x65 == test_2_u8[1] -> 1
0x43 == test_2_u8[2] -> 1
0x21 == test_2_u8[3] -> 1
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top