为什么 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。