【问题标题】:converting uint8_t[4] to uint32_t and back again in C在 C 中将 uint8_t[4] 转换为 uint32_t 并再次返回
【发布时间】:2014-05-26 00:24:13
【问题描述】:

我编写了这个测试来将uint8[4] 转换为uint32_t,但是如何将u32 的输出转换回原来的uint8[4]?我认为它可以通过位移以相同的方式完成,但我不确定如何......

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;
}

【问题讨论】:

  • 一个 union 非常适合这个。请注意 Little Endian/Big Endian 问题,以及可能的 MSb/LSb 排序。
  • 实际上,由于他的代码到目前为止与主机端无关,union 是个坏主意。

标签: c type-conversion


【解决方案1】:

要从uint8_tuint32_t 转换回来,我发现这些函数很有用:

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;
}

这是一个测试这些功能的程序:

#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;
}

我得到的输出是:

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

【讨论】:

    【解决方案2】:

    另一种可能的解决方案,对计算资源的要求最少,没有可怕的转换并且非常便携。

    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.
    

    【讨论】:

      【解决方案3】:

      我尝试做一些不同的事情,并使用已知的功能而不是自行实现。我的主要观点是告诉你字节位于内存中,你对它们的解释取决于上下文。请注意,应该注意一些字节顺序问题。此外,最好的教训是使用按位运算。

      #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;
      }
      

      输出是:

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

      反汇编【可以使用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
      

      【讨论】:

      • 感谢您提供的详细信息和改进的 cmets 实现。
      【解决方案4】:

      您的 u32() 函数是一个很好的练习,可以在与主机端无关的代码中读取大端数。
      它当然可以很容易地反转,只需从头开始,然后转到函数的开头,拆卸您的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);
      }
      

      【讨论】:

        【解决方案5】:

        使用位掩码。

        例如:

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

        对于 32 位术语,同样的想法也适用。只需掩盖您想要的区域,然后根据需要向右移位。

        【讨论】:

          猜你喜欢
          • 2017-06-13
          • 1970-01-01
          • 1970-01-01
          • 2010-09-12
          • 2014-07-23
          • 2011-10-30
          • 2014-11-13
          • 2015-01-19
          • 2013-02-18
          相关资源
          最近更新 更多