C uint16_t到填充的十六进制字符串,不带库

oknwwptz  于 2023-11-17  发布在  其他
关注(0)|答案(3)|浏览(121)

我尝试在不使用任何外部库的情况下,将uint16_t值转换为C中字符串的十六进制表示。到目前为止,我已经使用this答案实现了可靠的转换。然而,我的用例要求,如果uint16_t值小于0x1000,则结果输出需要用前导零填充。例如,如果我像这样调用函数:

char s[5];
itohexa(s, 1230); //(1230->0x04CE)

字符串
我会得到:

04CE


然而,这个函数给了我这个:

4CE


在字符串中表示的uint16_t中的实际字节数对我的用例很重要。我希望值偶尔会超过0x0FFF,所以只是在任何uint16_t数字中添加一个前导零字符(到目前为止,我一直在做的工作)是不够的。
我是这么做的:

static char *itohexa_helper(char *dest, uint16_t x, uint8_t pad) {
    if (x >= 16 || pad) {
        dest = itohexa_helper(dest, x/16, pad);
        pad = 1;
    }
    *dest++ = "0123456789ABCDEF"[x & 15];
    return dest;
}

char *itohexa(char *dest, uint16_t x) {
    uint8_t padding = (x < 0x1000);
    *itohexa_helper(dest, x, padding) = '\0';
    return dest;
}


在这种情况下,即使值的前4个最高有效位为零,paddingpad也不会进行转换吗?只是为了记录:对上述代码所做的修改仍然会产生与上面链接的answer中提到的原始结果相同的结果。

4dbbbstv

4dbbbstv1#

有时候,使用(更复杂的)递归来解决问题比使用迭代更有趣。你的代码很接近了!
你想要的不是“填充”,而是前导的“0”。设置、清除填充标志可能会起作用(如果正确完成),但只能给你给予一个前导零。
下面是你重新编写的代码:

static char *itohexa_helper( char *dest, uint16_t x, int n ) {
    if( --n )
        dest = itohexa_helper( dest, x>>4, n ); // recursion
    *dest++ = "0123456789ABCDEF"[x & 0xF]; // mask and offset
    return dest;
}

char *itohexa(char *dest, uint16_t x) {
    *itohexa_helper( dest, x, 4 ) = '\0'; // 4 digits of output...
    return dest;
}

int main( void ) {
    char buf[16]; // big enough

    puts( itohexa( buf, 0xBEEF ) );
    puts( itohexa( buf, 0xCDE ) );
    puts( itohexa( buf, 0xCD ) );

    return 0;
}

字符串
输出量:

BEEF
0CDE
00CD


注意没有任何base 10常量.

编辑

如果你100%确定(即0x0064%)你想要4个十六进制数字作为C字符串,那么有这样的:(它比我的竞争对手在这个友好的竞争中的代码短1条指令...:-)

char *itohexa(char *dest, uint16_t x) {
    // NB: index values shown are suitable for little endian processors.
    char *hex = "0123456789ABCDEF";
    union {
        uint16_t ui16;
        uint8_t c[2];
    } combo;
    dest[4] = '\0';
    combo.ui16 = ( x & 0x0F0F );
    dest[3] = hex[ combo.c[0] ];
    dest[1] = hex[ combo.c[1] ];
    combo.ui16 = ( x & 0xF0F0 )>>4;
    dest[2] = hex[ combo.c[0] ];
    dest[0] = hex[ combo.c[1] ];

    return dest;
}


你可能需要使用数组索引,这取决于MCU的大/小端。多有趣啊!!

webghufk

webghufk2#

一个简单的非递归方法:从最低有效位到最高有效位构建字符串。

#include <stdint.h>

char *itohexa(char *dest, uint16_t x) {
  int place = 4;
  dest[place] = '\0';
  while (place > 0) {
    dest[--place] = "0123456789ABCDEF"[x & 15];
    x >>= 4;
  }
  return dest;
}

字符串

apeeds0o

apeeds0o3#

我可能会尽可能明确地写出来,就像这样(在评论中解释):

#define ZERO_PAD false

char* itohexa (char dest[4+1], uint16_t x)
{
  bool remove_zeroes = true;
  char* ptr = dest;

  for(size_t i=0; i<4; i++)
  {
    // mask out the nibble by shifting 4 times byte number:
    uint8_t nibble = (x >> (3-i)*4) & 0xF; 
    
    // binary to ASCII hex conversion:
    char hex_digit = "0123456789ABCDEF" [nibble];
    
    if(!ZERO_PAD && remove_zeroes && hex_digit == '0')
    {
      ; // do nothing
    }
    else
    {
      remove_zeroes = false;
      *ptr = hex_digit;
      ptr++;
    }
  }

  if(remove_zeroes) // was it all zeroes?
  {
    *ptr = '0';
    ptr++;
  }
  *ptr = '\0';

  return dest;
}

字符串
测试用例:

char dest[4+1];
puts(itohexa(dest, 0));
puts(itohexa(dest, 1230));
puts(itohexa(dest, 0));
puts(itohexa(dest, 0xC1));
puts(itohexa(dest, 0xABBA));


输出ZERO_PAD false

0
4CE
0
C1
ABBA


输出ZERO_PAD true

0000
04CE
0000
00C1
ABBA


ZERO_PAD可以自然地作为一个参数,或者你可以创建两个不同的函数,等等。

相关问题