【问题标题】:printf() results in gibberishprintf() 导致乱码
【发布时间】:2019-02-26 07:17:26
【问题描述】:

我有这个代码:

unsigned char *command = "0000";
unsigned char foo = (hex_char_to_int(command[0]) << 4) | hex_char_to_int(command[1]);
unsigned char bar = (hex_char_to_int(command[2]) << 4) | hex_char_to_int(command[3]);
printf("foo: %02x, bar: %02x\r\n", foo, bar);

它使用这个功能:

unsigned char hex_char_to_int(unsigned char ch) {
    switch (ch){
        case '0': return 0;
        case '1': return 1;
        case '2': return 2;
        case '3': return 3;
        case '4': return 4;
        case '5': return 5;
        case '6': return 6;
        case '7': return 7;
        case '8': return 8;
        case '9': return 9;
        case 'A': return 0xA;
        case 'B': return 0xB;
        case 'C': return 0xC;
        case 'D': return 0xD;
        case 'E': return 0xE;
        case 'F': return 0xF;
        case 'a': return 0xA;
        case 'b': return 0xB;
        case 'c': return 0xC;
        case 'd': return 0xD;
        case 'e': return 0xE;
        case 'f': return 0xF;
        default: return 0;
    }
}

这是结果:

"JW\xd6\x96'$$LK\x90\xbbar: 3030\r\r\n"

这是在 Keil C51 编译器上,在 AT89C55WD 上,printf() 通过串行端口。

发生了什么事?

编辑

我把 printf 行改成 ​​

printf("%02x%02x\r\n", (unsigned int)foo, (unsigned int)bar);

所以它看起来像printf 中的一个错误。程序员们,请永远不要制作一个说谎的调试工具。求求你了。

【问题讨论】:

  • 您的示例中的命令后没有分号,所以我不确定这是否会编译。还有一个溢出 command[4] is not in command max index is 3.
  • 这是正在使用的代码的确切顺序(其中的值除外)吗?这似乎不会导致问题。
  • 还要检查非格式化字符串是否正确打印到串行控制台。
  • 不,我已经清理了代码以删除任何可以识别它的内容。 @JesusRamos 是的,其他字符串打印正确,其他格式指定甚至可以根据输入正常工作。
  • 听起来你的编译器不符合 C 语言标准——C 要求编译器在将unsigned char 作为参数传递给printf 等可变参数函数时将unsigned int 提升为unsigned int

标签: c embedded printf keil c51


【解决方案1】:

据我所知,该代码应该可以在任何符合标准的 C 编译器下工作。

我没有使用过 Keil C51,但我看到一些迹象表明它并不完全符合 C 标准的要求,例如在推广窄类型方面。

(此答案之前包含了许多可能的建议,其中大部分没有成功。如果您好奇,请查看编辑历史记录。)

显然,传递给printfunsigned char 参数没有提升为intunsigned int,正如c 标准所要求的那样。

要在保持代码合理可移植的同时解决此问题,请添加强制转换以将 foobar 的值显式转换为 unsigned int

printf("foo: %02x, bar: %02x\r\n", (unsigned int)foo, (unsigned int)bar);

\r 通常不需要,因为\n 会自动转换为系统的文本流行结束序列,但也许 Keil C51 的工作方式不同。)

同样,它应该可以工作,但是这个改变可能会解决 Keil 51 的 bug 特性。

更新:

我刚刚查看了 Keil C51 的在线文档。 printf 的文档显示了一些非标准特性,包括 bB 指定 char 类型,就像 l 指定 long 类型一样。

bB 在标准 C 中不是必需的,因为不可能将char(或unsigned char,或signed char)参数传递给printf;任何这样的论点都将被提升为int,或者可能是unsigned int。我由此推断,以及您遇到的错误,Keil C51 不会将窄参数提升为可变参数函数,特别是 unsigned char 参数提升为 @ 987654346@或unsigned int

这就解释了原因

printf("%02x", foo);

没用,为什么

printf("%02x", (unsigned int)foo);

做了。

此编译器以小型 8 位微处理器为目标。您不希望隐式扩展单字节参数是有道理的。作者显然选择了性能而不是一致性——这是一个完全有效的决定。 (如果文档对此更明确,或者我可能遗漏了一些东西,那就太好了。)

以十六进制打印unsigned char 值的推荐方法可能是:

printf("foo: %02bx, bar: %02bx\r\n", foo, bar);

请注意,这是 Keil C51 特有的,并且会使您的代码无法移植到其他平台。但话又说回来,为在如此小的系统上运行而编写的代码无论如何都不太可能是可移植的。

正如我之前建议的那样,转换为 unsigned int 也应该有效,但使用 "%02bx" 可能在时间和代码大小方面更有效,因为参数可以作为单个字节传递。

【讨论】:

  • +1,因为我几乎从不怀疑编译器错误是一个特性:-)
  • @jxh 更多类似警告未启用。
  • 不幸的是,进行此更改只会导致不同的乱码:(
  • @Drew:我添加了另外几个想法。 sizeof (unsigned int) 是什么?
  • @KeithThompson 我尝试更改 printf 中的文本,现在可以使用了。如果可能的话,我仍然想知道根本原因,但至少问题已经解决了。 sizeof(unsigned int) 在这个芯片上是 2。
猜你喜欢
  • 1970-01-01
  • 2022-01-08
  • 2022-01-08
  • 1970-01-01
  • 1970-01-01
  • 2015-09-03
  • 2016-08-02
  • 2013-09-14
相关资源
最近更新 更多