我在8位EFM8 MCU上使用Keil Cx51编译器。
如果我有一个功能,foo()
,就像这样:
xdata char buf[32];
void foo(char *msg, ...)
{
va_list args;
va_start (args, msg);
vsprintf(buf, msg, args);
va_end(args);
}
我这样使用它:
foo("My number is %d", 1);
buf
将包含:"我的号码 256 "。
如果我将通话更改为:
foo("My number is %d", (uint16_t)1);
然后buf
将包含"我的号码 1 ",正如所料。
为什么vsprintf()
在没有强制转换的情况下将数字1(0x0001)向左移位8位到256(0x0100)?这是一个字节序问题吗?
答案 0 :(得分:2)
已知问题是,具有“...
”参数列表的函数中的参数的数据类型很重要,因为编译器通常无法自动调整数据类型。
也许某些编译器会自动检测printf
中的格式说明符,并相应地调整参数中的数据类型。例如,GNU C将解析字符串并打印一条数据类型不匹配的警告消息,但不调整数据类型。
如果您不使用正确的数据类型,肯定会得到不好的结果。
显然%d
表示编译器为16位,1
是其他数据类型(例如8位)。
可能在您的编译器中有以下形式的参数列表:
(uint8)a, (uint16)b, (uint8)c, (uint8)d
可以像这样存储在内存中:
a, high_byte_of(b), low_byte_of(b), c, d
如果函数期望c
为16位值,则函数会将内存中的c
解释为c
的高8位,它将解释{{1}在参数列表中,d
...
因此,如果您执行以下操作,请执行以下操作:
d
内存看起来像这样:
foo("%d, %d", 1, 2, 3, 4);
...而1, 2, 3, 4, ...
将此解释为0x102和0x304,而不是1和2。
当然在RAM中,vsprintf
后跟一些与程序的这一部分无关的其他字节,因此RAM内容可能如下所示:
4
在你的情况下,RAM中的第一个字节是1, 2, 3, 4, 19, 32, 54, 21, 32, ...
,后跟一些“随机”数据(与函数调用无关的数据)。
显然1
之后的第一个字节是1
,因此0
函数将其解释为0x0100。
如果使用vsprintf
作为参数,则(uint16)1
后跟0
的字节将写入RAM,因此1
将其解释为0x0001。