printf()导致乱码

时间:2013-08-14 20:23:51

标签: 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中的错误。请程序员,永远不要做一个谎言的调试工具。求求你。

1 个答案:

答案 0 :(得分:4)

据我所知,该代码应该在任何符合C编译器的情况下工作。

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

(此答案之前包含了许多可能的建议,其中大部分内容都没有成功。如果您感到好奇,请参阅编辑记录。)

显然,传递给unsigned char的{​​{1}}参数提升为printfint,正如c标准要求的那样。

要在保持代码合理可移植性的同时解决此问题,请添加强制转换以明确将unsigned intfoo的值转换为bar

unsigned int

printf("foo: %02x, bar: %02x\r\n", (unsigned int)foo, (unsigned int)bar); 通常不是必需的,因为\r会自动转换为系统文本流的行结束序列,但也许Keil C51的工作方式不同。)

同样,应该以任何一种方式工作,但这种改变可能会解决Keil 51的 bug 功能。

更新:

我刚刚查看了Keil C51的在线文档。 printf的文档显示了一些非标准功能,包括\nb以指定B类型,就像char指定l类型一样

标准C中不需要

longb,因为无法将B(或charunsigned char)参数传递给signed char;任何此类论点都将提升为printf,或可能int。我从中推断出,并且从你遇到的错误中可以看出,Keil C51并没有提升可变参数函数的狭义参数,特别是unsigned int参数被提升到unsigned charint

这解释了为什么

unsigned int

不起作用,为什么

printf("%02x", foo);

确实

该编译器针对一个小型8位微处理器。有意义的是,您不希望隐式地扩展单字节参数。作者显然选择了绩效而不是一致性 - 这是一个非常有效的决定。 (如果文档对此更加明确,或者我错过了某些内容,那就太好了。)

以十六进制格式打印printf("%02x", (unsigned int)foo); 值的推荐方法可能是:

unsigned char

请注意,这是特定于Keil C51的,并使您的代码不可移植到其他平台。但话说回来,编写在这么小的系统上运行的代码无论如何都不太可移植。

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