为什么 vsprintf() 对 8 位数字进行位移?
Why is vsprintf() bit-shifting an 8-bit number?
我在 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
将包含:"My number is 256"。
如果我将调用更改为:
foo("My number is %d", (uint16_t)1);
然后 buf
将包含 "My number is 1",正如预期的那样。
为什么 vsprintf()
将数字 1 (0x0001) 左移 8 位到 256 (0x0100) 而无需强制转换?这是字节顺序问题吗?
具有“...
”参数列表的函数中参数的数据类型很重要,这是一个已知问题,因为编译器通常无法自动调整数据类型。
也许 一些编译器会自动检测 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 位,并且它将解释 d
在参数列表中作为 d
...
的低 8 位
所以在您的情况下,如果您执行以下操作:
foo("%d, %d", 1, 2, 3, 4);
内存看起来像这样:
1, 2, 3, 4, ...
... 和 vsprintf
会将其解释为 0x102 和 0x304,而不是 1 和 2。
当然在 RAM 中,4
后面跟着一些与程序的这部分无关的其他字节,因此 RAM 内容可能如下所示:
1, 2, 3, 4, 19, 32, 54, 21, 32, ...
在您的情况下,RAM 中的第一个字节是 1
,后跟一些 "random" 数据(与函数调用无关的数据)。
显然 1
之后的第一个字节是 0
所以 vsprintf
函数将其解释为 0x0100.
如果您使用 (uint16)1
作为参数,字节 0
后跟 1
将被写入 RAM,因此 vsprintf
将其解释为 0x0001。
我在 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
将包含:"My number is 256"。
如果我将调用更改为:
foo("My number is %d", (uint16_t)1);
然后 buf
将包含 "My number is 1",正如预期的那样。
为什么 vsprintf()
将数字 1 (0x0001) 左移 8 位到 256 (0x0100) 而无需强制转换?这是字节顺序问题吗?
具有“...
”参数列表的函数中参数的数据类型很重要,这是一个已知问题,因为编译器通常无法自动调整数据类型。
也许 一些编译器会自动检测 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 位,并且它将解释 d
在参数列表中作为 d
...
所以在您的情况下,如果您执行以下操作:
foo("%d, %d", 1, 2, 3, 4);
内存看起来像这样:
1, 2, 3, 4, ...
... 和 vsprintf
会将其解释为 0x102 和 0x304,而不是 1 和 2。
当然在 RAM 中,4
后面跟着一些与程序的这部分无关的其他字节,因此 RAM 内容可能如下所示:
1, 2, 3, 4, 19, 32, 54, 21, 32, ...
在您的情况下,RAM 中的第一个字节是 1
,后跟一些 "random" 数据(与函数调用无关的数据)。
显然 1
之后的第一个字节是 0
所以 vsprintf
函数将其解释为 0x0100.
如果您使用 (uint16)1
作为参数,字节 0
后跟 1
将被写入 RAM,因此 vsprintf
将其解释为 0x0001。