【问题标题】:Why doesn't sprintf want to fill my buffer?为什么 sprintf 不想填充我的缓冲区?
【发布时间】:2021-11-15 18:47:32
【问题描述】:

我使用sprintf() 填充缓冲区,然后通过 UART 发送此缓冲区。

我已经关注了sprintf 的许多示例和用例,但我的代码似乎不起作用

这是我的代码示例:

#include "stdio.h"
#include "usart.h"
#define Uart husart1

int main(void)
{
    uint32_t m = 3;
    char c = 'A';
    uint32_t o = 1;
    char buffer[256];
    sprintf(buffer, "AT+,%lu,%c,%lu\r\n", m, c, o);
    HAL_UART_Transmit(&Uart, (uint8_t*) buffer, strlen(buffer), 1000);
}

有了这段代码,我的输出是AT+,lu,,lu

你知道出了什么问题吗?

编辑 1: 当我用%d 替换%lu 时,我的编译器说这个错误:

format '%d' expects argument of type 'int', but argument 5 has type 'uint32_t {aka long unsigned int}' [-Wformat=]

所以我有%ld,缓冲区发送就像AT+,ld,,ld

编辑 2: 当我用%u 替换%lu 时,我的编译器说这个错误:

format '%u' expects argument of type 'unsigned int', but argument 3 has type 'uint32_t {aka long unsigned int}' [-Wformat=]

编辑 3: 与#include <stdio.h> 我有同样的错误

编辑 4:关于我想保留 uint32_t 的答案,因为我的 AT 参数可以是 32 位长

编辑 5: 我尝试使用 %d 和 (int16_t) 将类型转换如下:

sprintf(buffer, "AT+,%d,%c,%d\r\n", (int16_t) m, c, (int16_t) o);

这次我的结果是"AT+, ,A, "

为什么我的代码对有符号或无符号整数感到不安?

编辑 6:

使用 inttypes.h,它看起来像这样:

sprintf(buffer, "AT+,%"PRIu32",%c,%"PRIu32"\r\n", m, c, o);

我的输出仍然像 AT+,lu,,lu

【问题讨论】:

  • 您使用什么编译器和编译器选项?你在什么系统上运行?具体来说,您使用的是sprintf 的什么实现?
  • 取决于架构 %lu(特别是“long”的 l)对于 32 位 int 可能是错误的。这确实会导致各种麻烦(当 sprintf 实现也从堆栈中读取接下来的 4 个字节时,例如格式字符串的字节)。
  • 对@Peter-ReinstateMonica 提到的内容进行简单测试:将uint32_ts 改为ints 和sprintf(buffer, "AT+,%d,%c,%d\r\n", m, c, o);。我敢打赌,所有AT 命令都可以使用ints 创建。
  • 正确的(即可移植的)转换是使用 inttypes.h 中有些笨拙的定义,参见 stackoverflow.com/a/3168298/3150802。
  • 关于#include "stdio.h" - 你有修改过的本地stdio.h 吗?为什么不<stdio.h>?

标签: c printf uart


【解决方案1】:

uint32_t 的正确格式说明符位于定义宏 PRIu32 中,该宏扩展为正确的格式说明符字符串。它位于 inttypes.h 中。所以你的代码变成了这样(也修复了包含括号,假设这些头文件都不是你的项目的一部分,而是工具链的一部分):

#include <stdio.h>
#include <inttypes.h>
#include <usart.h>

#define Uart husart1

int main(void)
{
    uint32_t m = 3;
    char c = 'A';
    uint32_t o = 1;
    char buffer[256];
    sprintf(buffer, "AT+,%"PRIu32",%c,%"PRIu32"\r\n", m, c, o);
    HAL_UART_Transmit(&Uart, (uint8_t*) buffer, strlen(buffer), 1000);
}

宏可能看起来有点混乱,所以解释一下。在 C 中,您可以在编译时连接字符串文字,只需将它们一个接一个地放置即可。换句话说,"count is %" "lu" " items" 产生与"count is %lu items" 完全相同的字符串文字。并且 inttypes.h 包含类似 #define PRIu32 "u" 或平台上任何正确格式的定义。


参考链接:https://en.cppreference.com/w/c/types/integer


如果这不起作用,您有不符合标准的库。这可能发生在嵌入式平台上。您需要以其他方式构造字符串,而不是使用 printf 来处理它不支持的类型。您可以查阅您的标准库文档,也可以编写自己的函数,例如使用strncat 作为模型,您可以创建类似的函数

char *strncat_u32(char *dest, uint32_t value, size_t n);

【讨论】:

  • 我在我的编辑 n°6 中有这个命题的答案
  • @Clément 添加了一段关于此的内容
  • 谢谢!顺便说一句,在我的项目的其他部分中使用了 %lu 并且它有效,所以我不知道为什么这个文件不想使用与其他文件相同的格式
  • @Clément 如果它是相同的编译器,具有相同的命令行选项,并且您在相同的目标平台上运行编译后的程序,仍然有一个“正常”printf,而另一个有一些显然非常有限的版本......这确实很有趣。但是如果没有所有细节,就不可能知道问题出在哪里。
【解决方案2】:

嵌入式目标通常使用标准库的 nano 实现,它不支持许多 printf 格式。

只需将%u 用于uint32_t,因为uint32_t 是STM32 uC 的本机整数大小。

如果您想要非常“符合”,请将演员表添加到 unsigned int。

【讨论】:

  • @0___________ 显然,可以在不提供这些标准定义的设备上拥有自己的字符串定义,并继续编写可移植(和正确)代码。你不需要接触 glibc。
  • 与板子无关。它符合 C99+ 标准。如果您不是在 C99 模式下编译或不使用符合 C99+ 的编译器,那么显然它不可用。获得更好的编译器
  • 远离繁重的 stdlib 函数的建议可能很好——也许你可以为这样的用例添加你推荐的内容。 itoa()(如果有)?自己动手?
  • @Peter-ReinstateMonica 取决于项目。通常,我使用自己简化的 printf 实现。请记住,您也没有文件系统 + 没有动态内存分配(内存池除外 - 但它确实需要您自己的实现)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-02-20
  • 2011-04-24
  • 2013-02-21
  • 1970-01-01
  • 1970-01-01
  • 2014-10-21
  • 1970-01-01
相关资源
最近更新 更多