如何将 uint32_t 数字移动到 char[]?

Posted

技术标签:

【中文标题】如何将 uint32_t 数字移动到 char[]?【英文标题】:How to move uint32_t number to char[]? 【发布时间】:2021-09-04 07:43:21 【问题描述】:

我必须将 uint32_t 数字复制到 char[] 缓冲区的中间。 情况是这样的:

char buf[100];    
uint8_t pos = 52; // position in buffer to which I want to copy my uint32_t number
uint32_t seconds = 23456; // the actual number

我尝试像这样使用 memcpy:

memcpy(&buf[position], &seconds, sizeof(seconds));

但是在缓冲区中我得到了一些奇怪的字符,而不是我想要的数字

我也尝试过使用字节移位

int shiftby = 32;
for (int i = 0; i < 8; i++)

  buf[position++] = (seconds >> (shiftby -= 4)) & 0xF;

有没有其他方法可以解决这个问题?

【问题讨论】:

您的预期输出是什么?您的 memcpy 代码看起来不错。 您的循环在正确的轨道上,但存在一些问题 - 例如,buf[position] 是一个 byte(8 位),而您正在编写 nibbles i>(4 位)。 请提供complete minimal reproducible example,包括用于查看“一些奇怪字符”的测试代码以及​​确切的实际结果与预期结果。 将数字放入char 数组不会将数字的数字作为字符。如果你想要格式化的数字,你需要使用sprintf() 您的示例代码表明uint32_t 实际上是一个 容器 用于 8 位 BCD 表示或您尝试以十六进制表示值。如果是这种情况,你需要清楚,否则大多数人会假设你想要一个十进制表示,在这种情况下,输出的位数将是可变的,多达 10 个,所以它引出了字段宽度、填充的问题,左/右对齐等。简而言之,您的要求尚未明确。预期输出字符串的一些示例可能有助于澄清。 【参考方案1】:

您在memcpy 代码中所做的是将值23456 放入buff,从第52 个字节开始(所以是第52-55 个字节,因为seconds 的大小是4 个字节)。您想要做的(如果我理解正确的话)是将 string "23456" 放入 buff,从第 52 个字节开始。在第二种情况下,每个字符占用一个字节,并且每个byte 将保存其字符的 ASCII 值。

可能最好的方法是使用snprintf

int snprintf(char *buffer, size_t n, const char *format-string,
             argument-list);

在您的示例中: snprintf(&amp;buff[position], 5, "%d", seconds) 请注意,n 参数包含位数,而不是变量的大小。正如我所说 - 每个数字/字符占用一个字节。

显然你应该计算seconds中的位数而不是硬编码它是否可以改变,你还应该检查snprintf的返回值,看看操作是否成功

【讨论】:

感谢您的解决方案!但是,此代码适用于嵌入式系统(STM32),性能可以吗?听说 sprintf 和类似的函数可以从根本上减少它 @yeuop 没有注意到嵌入式标签...嵌入式并不是我的强项。我不确定使用 snprintf 的性能成本是多少,但如果它很高,则可能存在更适合嵌入式环境的 snprintf 等的“轻量级”实现,否则您将不得不寻找替代方案。希望对此事有更多了解的人可以发表评论并对此有所了解。其他选择是您“手动”执行,即获取每个数字,找出其 ASCII 值并将其放入缓冲区,但我认为有更好的选择。 @yeuop ;事实上,snprintf() et al` 可能在占用空间方面令人望而却步,如果您还没有在其他地方使用格式化输出,那么仅仅为此添加它会添加大量代码而几乎没有什么好处。如果您已经在使用格式化输出(printf()、sprintf()` 等),那么使用它可能几乎没有额外的开销,因为它们都共享大量代码。我会发布答案,但目前您的要求尚不清楚,如我对您问题的评论中所述。 我们在 ARM Cortex-M4 中使用 snprintf() 没有任何问题。但是这个芯片有2mb flash。我找不到我们包含的 lib 实现,但它必须是简化的。查看构建结果,我看到了最主要的功能:_svfprintf_r 在 3,91KB 和 _vfiprintf_r 在 2.17KB。 @yeuop 只需使用我在此处***.com/questions/68964088/… 发布到您上一个问题的版本即可。如果您想要十进制数字而不是十六进制,只需调整代码以将 '0' 添加到每个原始二进制数字。【参考方案2】:

目前尚不清楚您打算如何表示此uint32_t,但您的代码片段表明您期望使用十六进制(或者可能是 BCD)。在这种情况下:

for( int shiftby = 28; shiftby >= 0 ; shiftby -= 4 )

    char hexdigit = (seconds >> shiftby) & 0xF ;
    buf[position++] = hexdigit < 10 ? hexdigit + '0' : hexdigit + 'A' - 10 ;

请注意,此代码与您的代码之间唯一真正的区别是通过有条件地添加'0''A' - 10 转换为十六进制数字字符。使用shiftby 作为循环控制变量只是一种简化或您的算法。

您的代码的问题在于它将整数值 0 到 15 插入到 buf 中,并且与这些值关联的字符都是 ASCII 控制字符,名义上是非打印字符。它们如何或是否在任何特定显示器上呈现为字形取决于您用来呈现它们的内容。例如,在 Windows 控制台中,打印字符 0 到 15 会导致以下结果:

00 = <no glyph>
01 = '☺'
02 = '☻'
03 = '♥'
04 = '♦'
05 = '♣'
06 = '♠'
07 = <bell> (emits a sound, no glyph)
08 = <backspace>
09 = <tab>
10 = <linefeed>
11 = '♂'
12 = '♀'
13 = <carriage return>
14 = '♫'
15 = '☼'

上述更改将值 0 到 15 转换为 ASCII '0'-'9''A'-'F'

如果十六进制表示不是您想要的,那么您需要澄清问题。

注意,如果编码是BCD(Binary Coded Decimal),其中每个十进制位被编码为4位半字节,那么转换可以简化,因为值的范围减少到0到9:

char bcddigit = (seconds >> shiftby) & 0xF ;
buf[position++] = bcddigit + '0' ;

但十六进制转换也适用于 BCD。

【讨论】:

查找表版本应该是无分支的并且总体上更快:***.com/a/68965236/584518。以 17 字节 .rodata 闪存为代价。 @Lundin True。查找表也没有假设字符集的顺序 - 尽管这是一个相当学术的观点。代码会更简单,恢复一些查找表使用的空间。万一这是 BCD,我会选择算术解决方案。

以上是关于如何将 uint32_t 数字移动到 char[]?的主要内容,如果未能解决你的问题,请参考以下文章

如何将 32 字符(0/1)的序列转换为 32 位(uint32_t)?

如何将字符串转换为 uint32_t [重复]

如何在 uint64_t 中移动 >= 32 位?

uint8_t uint32_t 类型强制转换出错 以及 unsigned char 类型和 unsigned int 类型相互转化

如何在Java中获取与uint32_t对应的唯一值?

c++ uint32 如何转 const char