Printf函数格式化程序

时间:2019-06-03 09:34:58

标签: c++ integer printf overflow implicit-conversion

具有以下简单的C ++代码:

#include <stdio.h>

int main() {
    char c1 = 130;
    unsigned char c2 = 130;

    printf("1: %+u\n", c1);
    printf("2: %+u\n", c2);
    printf("3: %+d\n", c1);
    printf("4: %+d\n", c2);
    ...
    return 0;
}

输出如下:

1: 4294967170
2: 130
3: -126
4: +130

有人可以向我解释第一行和第三行的结果吗?

我正在使用具有所有默认设置的Linux gcc编译器。

3 个答案:

答案 0 :(得分:1)

char是8位。这意味着它可以表示2 ^ 8 = 256个唯一值。 uchar代表0到255,带符号的char代表-128到127(可以绝对代表任何东西,但这是典型的平台实现)。因此,为char分配130超出2的范围,并且当该值解释为时,该值溢出并将值包装为-126。编译器将130视为整数,并进行从charint的隐式转换。在大多数平台上,int是32位,符号位是MSB,值130很容易适合前8位,但随后编译器希望将24位切成小节以将其压缩为char。如果发生这种情况,并且您已经告诉编译器您想要一个带符号的char,则前8位的MSB实际上代表-128。哦哦!您现在在存储器char中有此存储器,当将其解释为带符号的char时,它是-128 + 2。我在平台上的皮棉对此尖叫。 。

angry linter

我将重点放在解释上,因为在内存中,两个值是相同的。您可以通过在1000 0010语句中强制转换值printf来确认这一点,然后再次看到130。

在第一个printf("3: %+d\n", (unsigned char)c1);语句中看到较大值的原因是,您正在将有符号的printf投射到无符号的char,其中int已经溢出。机器首先将char解释为-126,然后强制转换为无符号char,该数字不能表示该负值,因此可以得到有符号的{{ 1}}并减去126。

2 ^ 32-126 = 4294967170。 。宾果游戏

int语句2中,所有机器要做的就是添加24个零以达到32位,然后将该值解释为int。在语句一中,您已经告诉您您有一个带符号的值,因此它将首先将其转换为32位-126值,然后将-ve整数解释为无符号整数。再次,它翻转了如何解释最高有效位。有2个步骤:

  1. Signed char被提升为signed int,因为您想使用int。字符(可能已复制并)添加了24位。因为我们正在查看一个有符号的值,所以将发生一些机器指令来执行二进制补码,因此这里的内存看起来完全不同。
  2. 新的带符号的int内存被解释为无符号的,因此计算机查看MSB并将其解释为2 ^ 32,而不是促销中发生的-2 ^ 31。

琐事的有趣之处在于,如果您执行printf,则可以抑制叮当响的linter警告,但基于上述逻辑,您仍然会得到相同的垃圾(即,隐式转换会丢弃前24个位,而符号位无论如何都为零)。我已基于探索此问题(如果您确实想关注此问题,请发42137)提交了一份LLVM整洁的缺少功能报告

答案 1 :(得分:1)

(此答案假设您的计算机上char的范围是-128至127,unsigned char的范围是0至255,而unsigned int的范围是0至4294967295,碰巧是这种情况。)

char c1 = 130;

此处,130在char所代表的数字范围之外。 c1的值是实现定义的。在您的情况下,数字恰好“环绕”,将c1初始化为static_cast<char>(-126)

printf("1: %+u\n", c1);

c1被提升为int,从而产生-126。然后,%u说明符将其解释为unsigned int。这是未定义的行为。这次生成的数字恰好是unsigned int可以表示的唯一数字,与-126的4294967296模(即4294967170)一致。

printf("3: %+d\n", c1);

int说明符将-126%d直接解释为int,并按预期(?)输出-126。

答案 2 :(得分:1)

在情况1、2中,格式说明符与参数的类型不匹配,因此程序的行为是不确定的(在大多数系统上)。在大多数系统上,charunsigned char小于int,因此当它们作为可变参数传递时,它们会提升为int。 int与要求%u的格式说明符unsigned int不匹配。

unsigned charint一样大的异国系统(您的目标不是)上,它将升级为unsigned int,在这种情况下,4将具有UB需要int


3的解释很大程度上取决于实现指定的细节。结果取决于char是否签名,取决于可表示范围。

如果130是char的可表示值,例如当它是无符号类型时,则130将是正确的输出。事实并非如此,因此我们可以假设char是目标系统上的带符号类型。

使用无法代表的值(例如char用130表示)初始化有符号整数会导致实现定义的值。

在带正负号的2的补码表示形式的系统上(这是当今普遍存在的表示形式),实现定义的值通常是可表示的值,与不可表示的值乘以可表示的值的数量。 -126与130模256一致,并且是char的可表示值。