在下面的代码片段中,为什么我们得到 600 的输出?两个问题将帮助我理解这种行为。
我希望得到答案 88,这是循环超出 uint8_t 范围的结果
#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>
void main()
{
uint8_t b = 200;
printf("%" PRIu8 "\n",b+b+b);
printf("%" PRIu8 "\n",3*b);
}
gcc 版本 9.4.0 (Ubuntu 9.4.0-1ubuntu1~20.04.1)
在我看来这是一个库错误。
在 MS Visual Studio 中,宏
PRIu8
按原样扩展为 hhu
,您将获得预期的结果。
有趣的是,如果要使用 clang 那么你是否会编写 example
printf("%" PRIu8 "\n",( uint8_t)(b+b+b));
什么时候你会得到预期的结果。
在 C 中,无论何时调用带有省略号 (
...
) 参数的函数,例如 printf,所有这些参数都将进行 默认参数提升。这意味着任何较小的整数类型都将转换为 int
并以这种方式传递。所以在 printf 中,h
类型前缀是无关紧要的——它们将被 printf 忽略并且没有任何效果(因为参数必须是 int
无论如何)。
此外,任何算术运算符(如
+
或*
)都会发生较小整数类型的相同转换,并且生成的操作将以int
精度执行,给出int
结果。
要获得预期的结果,请将其显式屏蔽为适当的大小:
printf("%u\n", (b+b+b) & 0xff);
如果我们看一下 inttypes.h,我们会发现以下内容:
# define PRIu8 "u"
所以这个格式说明符不包含任何长度修饰符。这在其相应参数的上下文中是有意义的,因为不可能将
uint8_t
传递给可变参数函数。因为这种类型的等级比 int
小,所以这种类型的值将提升为 int
。这种提升也发生在表达式b+b+b
和3*b
中。
如果你明确地为一个
char
使用长度修饰符,你会得到预期的结果。
printf("%hhd\n",b+b+b);
printf("%hhd\n",3*b);