为什么未签名会被视为已签名?

Raf*_*fal 1 c language-lawyer

我知道已经回答了非常类似的问题,但我相信它不能解决我的问题。

unsigned char aaa = -10;
unsigned int bbb = (unsigned int)-5;
unsigned int ccc = (unsigned int)20 + (unsigned int)bbb;

printf("%d\n", aaa);
printf("%d\n", ccc);
Run Code Online (Sandbox Code Playgroud)

上面的代码打印aaa = 246(这是我所期望的),但这ccc = 15意味着unsigned int一直被视为签名。即使尝试可能过时的类型转换,我也找不到对此的解释。

Kam*_*Cuk 6

unsigned char aaa = -10;

int值将通过重复相加-10进行转换,直到结果在 的范围内。您的系统上很可能有 8 位,这意味着. 也是,在范围之内。所以。unsigned charUCHAR_MAX + 1[0, UCHAR_MAX]charUCHAR_MAX = 2**8 - 1 = 255-10 + UCHAR_MAX + 1246aaa = 246

unsigned int bbb = (unsigned int)-5;

添加,假设有 32 位,-5则结果为。UINT_MAX + 1intbbb = 4294967291

unsigned int ccc = (unsigned int)20 + (unsigned int)bbb;

无符号整数溢出“环绕”。那么就20 + 4294967291 = 4294967311更大了UINT_MAX = 2**32 - 1 = 4294967295。所以我们减去UINT_MAX+1直到我们在 range 内[0, UINT_MAX]。所以4294967311 - (UINT_MAX+1) = 15

printf("%d\n", aaa);

该代码很可能没问题。最有可能在您的平台上,在传递给可变参数函数参数之前unsigned char被提升为。int有关促销的参考,您可以阅读cppreference 隐式转换。因为%d期望intandunsigned char被提升为int,所以代码没问题。[1]

printf("%d\n", ccc);

此行会导致未定义的行为ccc具有类型unsigned int,而%d printf格式说明符需要一个signed int. 因为您的平台使用两位补码来表示数字,所以这只会导致printf将这些位解释为有符号值,无论如何15

[1]:理论上有可能unsigned char具有与 一样多的位int,因此unsigned char将被提升为unsigned int而不是int,这也会导致那里出现未定义的行为。