【问题标题】:Why is vsprintf() bit-shifting an 8-bit number?为什么 vsprintf() 对 8 位数字进行位移?
【发布时间】:2018-02-08 15:47:49
【问题描述】:

我在 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) 而不进行强制转换?这是字节序问题吗?

【问题讨论】:

  • 看起来更像是一个整数提升问题。 Keil 对此做了have a web page
  • 感谢您的链接。我只是按照说明禁用了整数提升并重新测试 - 问题仍然存在。我正在使用 Cx51 v9.53。
  • 这是倒退的,您希望启用它。请考虑发布汇编代码。
  • 默认开启。请参阅随附的程序集了解启用它的情况。
  • 问:Keil C 中1 表达式的数据类型是什么?而且,%d 期望什么数据类型?在 C 编程语言中,这两个问题的答案都是int;但是Keil C不是C。我已经很久没有为8052写代码了,但是如果我不得不猜测,我猜1的数据类型可能是char

标签: c printf 8-bit


【解决方案1】:

一个已知问题是,具有“...”参数列表的函数中参数的数据类型很重要,因为编译器通常无法自动调整数据类型。

也许一些编译器会自动检测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 解释为低8位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,后跟一些“随机”数据(与函数调用无关的数据)。

显然1 之后的第一个字节是0,因此vsprintf 函数将其解释为0x0100。

如果您使用(uint16)1 作为参数,则字节0 后跟1 将被写入RAM,因此vsprintf 将其解释为0x0001。

【讨论】:

  • 确实很有趣,感谢您的解释。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-07-07
  • 1970-01-01
  • 2016-06-30
  • 2021-08-13
  • 2013-07-24
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多