提问人:Boyfinn 提问时间:10/19/2023 最后编辑:Boyfinn 更新时间:10/20/2023 访问量:80
C uint16_t到没有库的填充十六进制字符串
C uint16_t to padded hex string without libraries
问:
我正在尝试将值转换为所述数字的十六进制表示形式,作为 C 中的字符串,而无需使用任何外部库。到目前为止,我已经通过这个答案实现了可靠的转换。
但是,我的用例要求,如果值小于 ,则使用前导零填充生成的输出。例如,如果我像这样调用函数:uint16_t
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 个最高有效位为零,and 是否仍会进行转换?
仅供记录:对上述代码所做的修改仍然会产生与上面链接的答案中提到的原始结果相同的结果。padding
pad
答:
我可能会尽可能明确地写出来,就像这样(评论中的解释):
#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
可以自然地做成一个参数,或者你可以做两个不同的函数,等等。
评论
uint16_t
do/while()
有时,使用(更复杂的)递归来解决问题比使用迭代更有趣。你和你的代码很接近!
与其说是你想要的“填充”,不如说是引导“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
请注意,没有任何 base10 常量...
编辑
如果你 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 的大/小端。多么有趣!!
评论
char buf[16];
8
一种简单的非递归方法:从最低有效数字到最高有效数字构建字符串。
#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;
}
评论
4
for (int digit = 0; digit < 4; digit++) { /* your code suitably changed */ }
dest[4] = '\0': for (int i = 3; 0 <= i; --i, x >>= 4) dest[i] = "0123456789ABCDEF"[x & 0xF]:
4CE
x < 0x1000
itohexa_helper
pad
pad
0x1000
15
16
pad
padding