printf()导致乱码
我有这个代码:
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);
它使用此function:
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
一个bug。 请程序员,永远不要做一个谎言的调试工具。 我求求你。
据我所知,该代码应该在任何符合C编译器的情况下工作。
我没有使用Keil C51,但我看到一些迹象表明它并不完全符合C标准的要求,例如在推广窄类型时。
(此答案之前包含了许多可能的建议,其中大多数都没有出现。如果您好奇,请参阅编辑历史记录。)
显然,传递给printf
的unsigned char
参数不会被提升为int
或unsigned int
,正如c标准所要求的那样。
要在保持代码合理可移植性的同时解决这个问题,添加强制转换以将foo
和bar
的值显式转换为unsigned int
:
printf("foo: %02x, bar: %02x\r\n", (unsigned int)foo, (unsigned int)bar);
( \r
通常不是必需的,因为\n
会自动转换为文本流的系统行结束序列,但也许Keil C51的工作方式不同。)
同样,它应该以任何一种方式工作,但这种改变可能适用于Keil 51的bugfunction。
更新:
我刚刚查看了Keil C51的在线文档。 printf的文档显示了一些非标准function,包括b
和B
来指定char
类型,就像l
指定long
类型一样。
标准C中不需要b
和B
,因为不可能将char
(或unsigned char
或signed char
)参数传递给printf
; 任何这样的参数都将被提升为int
,或者可能是unsigned int
。 我从中推断出,并且从你遇到的错误中,Keil C51不会提升变量函数的狭义参数,特别是unsigned char
参数不会被提升为int
或unsigned int
。
这解释了原因
printf("%02x", foo);
不起作用,为什么
printf("%02x", (unsigned int)foo);
没有。
该编译器针对一个小型8位微处理器。 有意义的是,您不希望隐式地扩展单字节参数。 作者显然选择了绩效而不是一致性 – 这是一个非常有效的决定。 (如果文档对此更加明确,或者我错过了某些内容,那就太好了。)
可能推荐的以hex方式打印unsigned char
值的方法是:
printf("foo: %02bx, bar: %02bx\r\n", foo, bar);
请注意,这是Keil C51特有的,使您的代码不可移植到其他平台。 但话说回来,编写在这么小的系统上运行的代码无论如何都不太可能是可移植的。
正如我之前建议的那样,转换为unsigned int
也应该可以工作,但是使用"%02bx"
可能在时间和代码大小上更有效,因为参数可以作为单个字节传递。