代码之家  ›  专栏  ›  技术社区  ›  Ed King

为什么vsprintf()位会移动8位数字?

  •  2
  • Ed King  · 技术社区  · 7 年前

    我在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);
    

    然后 缓冲器 将包含“我的号码是 1. “,如预期。

    为什么是 vsprintf() 是否将数字1(0x0001)左移8位至256(0x0100),而不进行转换?这是一个持久性问题吗?

    1 回复  |  直到 7 年前
        1
  •  2
  •   Martin Rosenau    7 年前

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

    大概 某些编译器将自动检测中的格式说明符 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 它会解释 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。