Answers:
简短答案
你i
将被转化通过添加为无符号整数UINT_MAX + 1
,则除了将与无符号值进行,产生大的result
(取决于的值u
和i
)。
长答案
根据C99标准:
6.3.1.8常规算术转换
- 如果两个操作数具有相同的类型,则无需进一步转换。
- 否则,如果两个操作数都具有符号整数类型或都具有无符号整数类型,则将具有较小整数转换等级的操作数转换为具有较大等级的操作数的类型。
- 否则,如果具有无符号整数类型的操作数的秩大于或等于另一个操作数的类型的秩,则将带符号整数类型的操作数转换为无符号整数类型的操作数的类型。
- 否则,如果带符号整数类型的操作数的类型可以表示带无符号整数类型的操作数的所有值,则带无符号整数类型的操作数将转换为带符号整数类型的操作数的类型。
- 否则,两个操作数都将转换为与带符号整数类型的操作数类型相对应的无符号整数类型。
在您的情况下,我们有一个unsigned int(u
)和signed int(i
)。参考上面的(3),由于两个操作数具有相同的等级,因此您i
将需要转换为无符号整数。
6.3.1.3有符号和无符号整数
- 将具有整数类型的值转换为_Bool以外的其他整数类型时,如果该值可以用新类型表示,则该值不变。
- 否则,如果新类型是无符号的,则通过重复添加或减去比新类型可表示的最大值多一个值来转换值,直到该值在新类型的范围内为止。
- 否则,将对新类型进行签名,并且无法在其中表示值;结果是实现定义的,还是引发实现定义的信号。
现在我们需要参考上面的(2)。i
通过添加,您将转换为无符号值UINT_MAX + 1
。因此,结果将取决于UINT_MAX
您的实现的定义方式。它会很大,但不会溢出,因为:
6.2.5(9)
涉及无符号操作数的计算永远不会溢出,因为无法用所得的无符号整数类型表示的结果的模数要比该所得的类型可以表示的最大值的模数大。
奖励:算术转换半WTF
#include <stdio.h>
int main(void)
{
unsigned int plus_one = 1;
int minus_one = -1;
if(plus_one < minus_one)
printf("1 < -1");
else
printf("boring");
return 0;
}
您可以使用此链接在线尝试:https : //repl.it/repls/QuickWhimsicalBytes
奖励:算术转换的副作用
UINT_MAX
通过将无符号值初始化为-1
,可以使用算术转换规则来获取的值,即:
unsigned int umax = -1; // umax set to UINT_MAX
由于上述转换规则,因此保证了无论系统的带符号号码表示方式都可移植。有关更多信息,请参见此SO问题:使用-1将所有位设置为true是否安全?
转化率从符号到无符号确实不是一定只复制或重新解释的符号值的表示。引用C标准(C99 6.3.1.3):
将具有整数类型的值转换为_Bool以外的其他整数类型时,如果该值可以用新类型表示,则该值不变。
否则,如果新类型是无符号的,则通过重复添加或减去比新类型可表示的最大值多一个值来转换值,直到该值在新类型的范围内为止。
否则,将对新类型进行签名,并且无法在其中表示值;结果是实现定义的,还是引发实现定义的信号。
对于如今近乎普遍的二进制补码表示,规则确实对应于重新解释位。但是对于其他表示形式(符号和大小或1的补码),C实现必须仍然安排相同的结果,这意味着转换不能只是复制这些位。例如,(unsigned)-1 == UINT_MAX,与表示形式无关。
通常,将C中的转换定义为对值而不是表示形式进行操作。
要回答原始问题:
unsigned int u = 1234;
int i = -5678;
unsigned int result = u + i;
i的值转换为unsigned int,产生UINT_MAX + 1 - 5678
。然后将此值添加到无符号值1234,产生UINT_MAX + 1 - 4444
。
(与无符号溢出不同,有符号溢出会调用未定义的行为。环绕是很常见的,但是C标准不能保证这种环绕-编译器的优化可能会对进行不必要假设的代码造成严重破坏。)
当添加一个无符号变量和一个带符号变量(或任何二进制操作)时,二者都隐式转换为无符号,这将导致巨大的结果。
因此,从结果上可能是巨大的和错误的意义上讲,这是安全的,但绝不会崩溃。
这里发生了什么隐式转换,
我将被转换为无符号整数。
这段代码对u和i的所有值安全吗?
在明确定义的意义上是安全的(请参阅https://stackoverflow.com/a/50632/5083516)。
规则通常用难以理解的标准来编写,但是本质上无论有符号整数中使用了哪种表示形式,无符号整数都将包含数字的2的补码表示形式。
加,减和乘将在这些数字上正常工作,从而导致另一个无符号整数包含表示“实际结果”的二进制补码。
除法和转换为较大的无符号整数类型将具有定义明确的结果,但这些结果将不是“真实结果”的2的补码表示。
(安全,就算这个示例中的结果将溢出到一个巨大的正数,我也可以将其强制转换为int并获得真实的结果。)
虽然标准定义了从有符号到无符号的转换,但是反向实现是由实现定义的,gcc和msvc都定义了转换,这样当将无符号整数中存储的2的补码转换回有符号整数时,您将获得“真实结果” 。我希望您只会在不使用2的补码作为有符号整数的晦涩系统上找到其他行为。
https://gcc.gnu.org/onlinedocs/gcc/Integers-implementation.html#Integers-implementation https://msdn.microsoft.com/zh-CN/library/0eex498h.aspx
可怕的答案
Ozgur Ozcitak
当您从有符号转换为无符号(反之亦然)时,数字的内部表示形式不会更改。变化的是编译器如何解释符号位。
这是完全错误的。
马特斯·弗雷德里克森
当添加一个无符号变量和一个带符号变量(或任何二进制操作)时,二者都隐式转换为无符号,这将导致巨大的结果。
这也是错误的。由于无符号类型中的填充位,如果无符号整数具有相同的精度,则可以将它们提升为整数。
h
您的加法运算将int转换为unsigned int。
错误。也许会,也许不会。
从unsigned int到signed int的转换取决于实现。(但是,这几天可能在大多数平台上都可以达到预期的效果。)
错误。如果它导致溢出或保留值,则为未定义行为。
匿名
i的值转换为unsigned int ...
错误。取决于int相对于unsigned int的精度。
泰勒·普莱斯
如先前的回答,您可以在有符号和无符号之间来回转换,而不会出现问题。
错误。试图存储有符号整数范围之外的值会导致未定义的行为。
现在我终于可以回答这个问题了。
如果int的精度等于unsigned int,则u将被提升为有符号int,您将从表达式(u + i)中获得值-4444。现在,如果u和i具有其他值,则可能会出现溢出和未定义的行为,但是使用这些确切的数字,您将得到-4444 [1]。该值的类型为int。但是您正在尝试将该值存储到一个无符号的int中,以便随后将其转换为一个无符号的int,结果最终将具有(UINT_MAX + 1)-4444。
如果unsigned int的精度大于int的精度,则有符号的int将被提升为一个无符号的int,并产生值(UINT_MAX + 1)-5678,该值将与另一个无符号的int 1234相加。其他值,这些表达式使表达式落在{0..UINT_MAX}范围之外。将添加或减去值(UINT_MAX + 1),直到结果DOES落在{0..UINT_MAX)范围内,并且不会发生未定义的行为。
什么是精度?
整数具有填充位,符号位和值位。无符号整数显然没有符号位。进一步保证了无符号字符没有填充位。整数具有的值位数是其精度。
[Gotchas]
如果存在填充位,则不能单独使用macrosizeof宏来确定整数的精度。并且字节的大小不必是C99定义的八位位组(八位)。
[1]溢出可能发生在两个点之一。加法之前(升级期间)中的任何一个-当您的unsigned int太大而无法容纳在int内部时。即使无符号int在int范围内,加法后也可能发生溢出,加法后结果仍然可能溢出。
int
会转换为unsigned int
。
int
或unsigned int
于那些类型的东西类型中的一种unsigned int
或int
预期。在TC2中添加了“或等于”,以允许枚举类型的转换等级等于int
或unsigned int
转换为这些类型之一。从来没有想过所描述的促销会在unsigned int
和之间转换int
。unsigned int
和之间的通用类型确定int
仍受6.3.1.8(即使在TC2之后)支配。