如何将无符号数转换为有符号数的 C 语言类型转换工作原理?

6
这段代码使用的是哪种语言标准,可以打印出“-1”?
unsigned int u = UINT_MAX;
signed   int s = u;
printf("%d", s);

https://en.cppreference.com/w/c/language/conversion

否则,如果目标类型是有符号的,则行为是实现定义的(可能包括引发信号)。

https://gcc.gnu.org/onlinedocs/gcc/Integers-implementation.html#Integers-implementation

GCC仅支持二进制补码整数类型,所有位模式都是普通值。
将整数转换为有符号整数类型时,如果该值无法在该类型的对象中表示(C90 6.2.1.2、C99和C11 6.3.1.3),则产生的结果或引发的信号如下:
对于转换为宽度为N的类型,该值被模2^N减小以使其在类型范围内;不引发信号。
因此,对我来说,将UINT_MAX转换为int意味着将UINT_MAX除以2^(CHAR_BIT * sizeof(int))。举个例子,对于32位的整数,0xFFFFFFFF / 2^32 = 0xFFFFFFFF。所以这并没有真正解释值“-1”是如何出现在int中的。
是否有其他语言规定,在模除之后我们只需重新解释位?或者有其他标准的部分优先于我引用的部分吗?

你已经找到了相关部分,C17 6.3.1.3/3:“否则,新类型为带符号类型且该值无法表示;结果为实现定义或引发实现定义的信号。”这意味着由编译器决定如何处理。随着即将推出的C2x标准,他们最终打算放弃对奇异有符号格式的支持,只允许2的补码。这反过来意味着很多不良定义的行为可以变成良好定义的行为。 - Lundin
1个回答

7
C标准的任何部分都不能保证您的代码通常会打印-1。正如它所说,转换的结果是实现定义的。但是,GCC文档确实承诺,如果您使用他们的实现进行编译,那么您的代码将打印-1。这与位模式无关,只涉及数学。
在GCC手册中,“对2^N取模”明显意图的解释是,结果应该是范围为signed int的唯一数字,它关于输入模2^N同余。这是一种精确的数学方式来定义您所期望的“环绕”行为,它恰好与通过重新解释位获取的结果相符合。
假设32位,UINT_MAX的值为4294967295。这关于4294967296模等于-1。也就是说,4294967295和-1之间的差是4294967296的倍数,即4294967296本身。此外,这必须是[-2147483648, 2147483647]中唯一的这样一个数字。(任何其他的与-1同余的数都至少为-1 + 4294967296 = 4294967295,或者最多为-1 - 4294967296 = -4294967297)。因此,-1是转换的结果。
换句话说,反复添加或减去4294967296,直到得到一个在signed int范围内的数字。保证只有一个这样的数字存在,在这种情况下是-1。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接