c - printf() 导致乱码

标签 c embedded printf keil c51

我有这个代码:

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"

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

这是怎么回事?

编辑

我将 printf 行更改为

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

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

最佳答案

据我所知,该代码应该可以在任何符合标准的 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 的一个错误功能。

更新:

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

bB 在标准 C 中不是必需的,因为不可能传递 char(或 unsigned charsigned char) printf 的参数;任何此类参数都将被提升为 int,或者可能是 unsigned int。我从这个以及您遇到的错误中推断,Keil C51 不会将窄参数提升为可变参数函数,特别是 unsigned char 参数不是 提升为 intunsigned 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" 可能在时间和代码大小方面更有效,因为参数可以作为单个字节传递。

关于c - printf() 导致乱码,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18241576/

相关文章:

c - 如何在 C 中对另一个数组中的两个数组求和?

c - C 中的 char 是否具有预先分配的零索引值?

c - 处理内存访问时避免将整数转换为指针的最佳方法 (MISRA 2008)

c - 发现段错误,但正在优化之前的消息

c - printf ("%s")、printf(s) 和 fputs 之间有什么区别?

c - 0 精度的 printf 0

c - setfillcolor() 无法设置颜色?

c++ - printf 如何从 float 中提取数字?

linux - 在嵌入式 Linux 上安全写入紧凑型闪存

c++ - 如何对嵌入式代码进行单元测试?